Branch data Line data Source code
1 : : /*
2 : : * Copyright (c) 2008, 2009, 2010, 2011, 2012, 2013, 2014, 2015 Nicira, Inc.
3 : : *
4 : : * Licensed under the Apache License, Version 2.0 (the "License");
5 : : * you may not use this file except in compliance with the License.
6 : : * You may obtain a copy of the License at:
7 : : *
8 : : * http://www.apache.org/licenses/LICENSE-2.0
9 : : *
10 : : * Unless required by applicable law or agreed to in writing, software
11 : : * distributed under the License is distributed on an "AS IS" BASIS,
12 : : * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 : : * See the License for the specific language governing permissions and
14 : : * limitations under the License.
15 : : */
16 : :
17 : : #include <config.h>
18 : :
19 : : #include "bond.h"
20 : :
21 : : #include <limits.h>
22 : : #include <stdint.h>
23 : : #include <stdlib.h>
24 : : #include <math.h>
25 : :
26 : : #include "connectivity.h"
27 : : #include "coverage.h"
28 : : #include "dp-packet.h"
29 : : #include "flow.h"
30 : : #include "openvswitch/hmap.h"
31 : : #include "lacp.h"
32 : : #include "netdev.h"
33 : : #include "odp-util.h"
34 : : #include "ofproto/ofproto-dpif.h"
35 : : #include "ofproto/ofproto-dpif-rid.h"
36 : : #include "ofproto/ofproto-provider.h"
37 : : #include "openvswitch/dynamic-string.h"
38 : : #include "openvswitch/list.h"
39 : : #include "openvswitch/match.h"
40 : : #include "openvswitch/ofp-actions.h"
41 : : #include "openvswitch/ofp-util.h"
42 : : #include "openvswitch/ofpbuf.h"
43 : : #include "openvswitch/vlog.h"
44 : : #include "packets.h"
45 : : #include "poll-loop.h"
46 : : #include "seq.h"
47 : : #include "openvswitch/shash.h"
48 : : #include "timeval.h"
49 : : #include "unixctl.h"
50 : : #include "util.h"
51 : :
52 : 1288 : VLOG_DEFINE_THIS_MODULE(bond);
53 : :
54 : : static struct ovs_rwlock rwlock = OVS_RWLOCK_INITIALIZER;
55 : : static struct hmap all_bonds__ = HMAP_INITIALIZER(&all_bonds__);
56 : : static struct hmap *const all_bonds OVS_GUARDED_BY(rwlock) = &all_bonds__;
57 : :
58 : : /* Bit-mask for hashing a flow down to a bucket. */
59 : : #define BOND_MASK 0xff
60 : : #define BOND_BUCKETS (BOND_MASK + 1)
61 : :
62 : : /* A hash bucket for mapping a flow to a slave.
63 : : * "struct bond" has an array of BOND_BUCKETS of these. */
64 : : struct bond_entry {
65 : : struct bond_slave *slave; /* Assigned slave, NULL if unassigned. */
66 : : uint64_t tx_bytes /* Count of bytes recently transmitted. */
67 : : OVS_GUARDED_BY(rwlock);
68 : : struct ovs_list list_node; /* In bond_slave's 'entries' list. */
69 : :
70 : : /* Recirculation.
71 : : *
72 : : * 'pr_rule' is the post-recirculation rule for this entry.
73 : : * 'pr_tx_bytes' is the most recently seen statistics for 'pr_rule', which
74 : : * is used to determine delta (applied to 'tx_bytes' above.) */
75 : : struct rule *pr_rule;
76 : : uint64_t pr_tx_bytes OVS_GUARDED_BY(rwlock);
77 : : };
78 : :
79 : : /* A bond slave, that is, one of the links comprising a bond. */
80 : : struct bond_slave {
81 : : struct hmap_node hmap_node; /* In struct bond's slaves hmap. */
82 : : struct ovs_list list_node; /* In struct bond's enabled_slaves list. */
83 : : struct bond *bond; /* The bond that contains this slave. */
84 : : void *aux; /* Client-provided handle for this slave. */
85 : :
86 : : struct netdev *netdev; /* Network device, owned by the client. */
87 : : uint64_t change_seq; /* Tracks changes in 'netdev'. */
88 : : ofp_port_t ofp_port; /* OpenFlow port number. */
89 : : char *name; /* Name (a copy of netdev_get_name(netdev)). */
90 : :
91 : : /* Link status. */
92 : : long long delay_expires; /* Time after which 'enabled' may change. */
93 : : bool enabled; /* May be chosen for flows? */
94 : : bool may_enable; /* Client considers this slave bondable. */
95 : :
96 : : /* Rebalancing info. Used only by bond_rebalance(). */
97 : : struct ovs_list bal_node; /* In bond_rebalance()'s 'bals' list. */
98 : : struct ovs_list entries; /* 'struct bond_entry's assigned here. */
99 : : uint64_t tx_bytes; /* Sum across 'tx_bytes' of entries. */
100 : : };
101 : :
102 : : /* A bond, that is, a set of network devices grouped to improve performance or
103 : : * robustness. */
104 : : struct bond {
105 : : struct hmap_node hmap_node; /* In 'all_bonds' hmap. */
106 : : char *name; /* Name provided by client. */
107 : : struct ofproto_dpif *ofproto; /* The bridge this bond belongs to. */
108 : :
109 : : /* Slaves. */
110 : : struct hmap slaves;
111 : :
112 : : /* Enabled slaves.
113 : : *
114 : : * Any reader or writer of 'enabled_slaves' must hold 'mutex'.
115 : : * (To prevent the bond_slave from disappearing they must also hold
116 : : * 'rwlock'.) */
117 : : struct ovs_mutex mutex OVS_ACQ_AFTER(rwlock);
118 : : struct ovs_list enabled_slaves OVS_GUARDED; /* Contains struct bond_slaves. */
119 : :
120 : : /* Bonding info. */
121 : : enum bond_mode balance; /* Balancing mode, one of BM_*. */
122 : : struct bond_slave *active_slave;
123 : : int updelay, downdelay; /* Delay before slave goes up/down, in ms. */
124 : : enum lacp_status lacp_status; /* Status of LACP negotiations. */
125 : : bool bond_revalidate; /* True if flows need revalidation. */
126 : : uint32_t basis; /* Basis for flow hash function. */
127 : :
128 : : /* SLB specific bonding info. */
129 : : struct bond_entry *hash; /* An array of BOND_BUCKETS elements. */
130 : : int rebalance_interval; /* Interval between rebalances, in ms. */
131 : : long long int next_rebalance; /* Next rebalancing time. */
132 : : bool send_learning_packets;
133 : : uint32_t recirc_id; /* Non zero if recirculation can be used.*/
134 : : struct hmap pr_rule_ops; /* Helps to maintain post recirculation rules.*/
135 : :
136 : : /* Store active slave to OVSDB. */
137 : : bool active_slave_changed; /* Set to true whenever the bond changes
138 : : active slave. It will be reset to false
139 : : after it is stored into OVSDB */
140 : :
141 : : /* Interface name may not be persistent across an OS reboot, use
142 : : * MAC address for identifing the active slave */
143 : : struct eth_addr active_slave_mac;
144 : : /* The MAC address of the active interface. */
145 : : /* Legacy compatibility. */
146 : : bool lacp_fallback_ab; /* Fallback to active-backup on LACP failure. */
147 : :
148 : : struct ovs_refcount ref_cnt;
149 : : };
150 : :
151 : : /* What to do with an bond_recirc_rule. */
152 : : enum bond_op {
153 : : ADD, /* Add the rule to ofproto's flow table. */
154 : : DEL, /* Delete the rule from the ofproto's flow table. */
155 : : };
156 : :
157 : : /* A rule to add to or delete from ofproto's internal flow table. */
158 : : struct bond_pr_rule_op {
159 : : struct hmap_node hmap_node;
160 : : struct match match;
161 : : ofp_port_t out_ofport;
162 : : enum bond_op op;
163 : : struct rule **pr_rule;
164 : : };
165 : :
166 : : static void bond_entry_reset(struct bond *) OVS_REQ_WRLOCK(rwlock);
167 : : static struct bond_slave *bond_slave_lookup(struct bond *, const void *slave_)
168 : : OVS_REQ_RDLOCK(rwlock);
169 : : static void bond_enable_slave(struct bond_slave *, bool enable)
170 : : OVS_REQ_WRLOCK(rwlock);
171 : : static void bond_link_status_update(struct bond_slave *)
172 : : OVS_REQ_WRLOCK(rwlock);
173 : : static void bond_choose_active_slave(struct bond *)
174 : : OVS_REQ_WRLOCK(rwlock);
175 : : static unsigned int bond_hash_src(const struct eth_addr mac,
176 : : uint16_t vlan, uint32_t basis);
177 : : static unsigned int bond_hash_tcp(const struct flow *, uint16_t vlan,
178 : : uint32_t basis);
179 : : static struct bond_entry *lookup_bond_entry(const struct bond *,
180 : : const struct flow *,
181 : : uint16_t vlan)
182 : : OVS_REQ_RDLOCK(rwlock);
183 : : static struct bond_slave *get_enabled_slave(struct bond *)
184 : : OVS_REQ_RDLOCK(rwlock);
185 : : static struct bond_slave *choose_output_slave(const struct bond *,
186 : : const struct flow *,
187 : : struct flow_wildcards *,
188 : : uint16_t vlan)
189 : : OVS_REQ_RDLOCK(rwlock);
190 : :
191 : : /* Attempts to parse 's' as the name of a bond balancing mode. If successful,
192 : : * stores the mode in '*balance' and returns true. Otherwise returns false
193 : : * without modifying '*balance'. */
194 : : bool
195 : 32 : bond_mode_from_string(enum bond_mode *balance, const char *s)
196 : : {
197 [ + + ]: 32 : if (!strcmp(s, bond_mode_to_string(BM_TCP))) {
198 : 16 : *balance = BM_TCP;
199 [ + + ]: 16 : } else if (!strcmp(s, bond_mode_to_string(BM_SLB))) {
200 : 2 : *balance = BM_SLB;
201 [ + - ]: 14 : } else if (!strcmp(s, bond_mode_to_string(BM_AB))) {
202 : 14 : *balance = BM_AB;
203 : : } else {
204 : 0 : return false;
205 : : }
206 : 32 : return true;
207 : : }
208 : :
209 : : /* Returns a string representing 'balance'. */
210 : : const char *
211 : 80 : bond_mode_to_string(enum bond_mode balance) {
212 [ + + + - ]: 80 : switch (balance) {
213 : : case BM_TCP:
214 : 44 : return "balance-tcp";
215 : : case BM_SLB:
216 : 16 : return "balance-slb";
217 : : case BM_AB:
218 : 20 : return "active-backup";
219 : : }
220 : 0 : OVS_NOT_REACHED();
221 : : }
222 : :
223 : :
224 : : /* Creates and returns a new bond whose configuration is initially taken from
225 : : * 's'.
226 : : *
227 : : * The caller should register each slave on the new bond by calling
228 : : * bond_slave_register(). */
229 : : struct bond *
230 : 19 : bond_create(const struct bond_settings *s, struct ofproto_dpif *ofproto)
231 : : {
232 : : struct bond *bond;
233 : :
234 : 19 : bond = xzalloc(sizeof *bond);
235 : 19 : bond->ofproto = ofproto;
236 : 19 : hmap_init(&bond->slaves);
237 : 19 : ovs_list_init(&bond->enabled_slaves);
238 : 19 : ovs_mutex_init(&bond->mutex);
239 : 19 : ovs_refcount_init(&bond->ref_cnt);
240 : 19 : hmap_init(&bond->pr_rule_ops);
241 : :
242 : 19 : bond_reconfigure(bond, s);
243 : 19 : return bond;
244 : : }
245 : :
246 : : struct bond *
247 : 6747 : bond_ref(const struct bond *bond_)
248 : : {
249 : 6747 : struct bond *bond = CONST_CAST(struct bond *, bond_);
250 : :
251 [ + - ]: 6747 : if (bond) {
252 : 6747 : ovs_refcount_ref(&bond->ref_cnt);
253 : : }
254 : 6747 : return bond;
255 : : }
256 : :
257 : : /* Frees 'bond'. */
258 : : void
259 : 234905 : bond_unref(struct bond *bond)
260 : : {
261 : : struct bond_pr_rule_op *pr_op;
262 : : struct bond_slave *slave;
263 : :
264 [ + + ][ + + ]: 234905 : if (!bond || ovs_refcount_unref_relaxed(&bond->ref_cnt) != 1) {
265 : 234887 : return;
266 : : }
267 : :
268 : 18 : ovs_rwlock_wrlock(&rwlock);
269 : 18 : hmap_remove(all_bonds, &bond->hmap_node);
270 : 18 : ovs_rwlock_unlock(&rwlock);
271 : :
272 [ + - ][ - + ]: 18 : HMAP_FOR_EACH_POP (slave, hmap_node, &bond->slaves) {
[ - + ]
273 : : /* Client owns 'slave->netdev'. */
274 : 0 : free(slave->name);
275 : 0 : free(slave);
276 : : }
277 : 18 : hmap_destroy(&bond->slaves);
278 : :
279 : 18 : ovs_mutex_destroy(&bond->mutex);
280 : 18 : free(bond->hash);
281 : 18 : free(bond->name);
282 : :
283 [ + + ][ - + ]: 1298 : HMAP_FOR_EACH_POP (pr_op, hmap_node, &bond->pr_rule_ops) {
[ + + ]
284 : 1280 : free(pr_op);
285 : : }
286 : 18 : hmap_destroy(&bond->pr_rule_ops);
287 : :
288 [ + + ]: 18 : if (bond->recirc_id) {
289 : 11 : recirc_free_id(bond->recirc_id);
290 : : }
291 : :
292 : 18 : free(bond);
293 : : }
294 : :
295 : : static void
296 : 1792 : add_pr_rule(struct bond *bond, const struct match *match,
297 : : ofp_port_t out_ofport, struct rule **rule)
298 : : {
299 : 1792 : uint32_t hash = match_hash(match, 0);
300 : : struct bond_pr_rule_op *pr_op;
301 : :
302 [ - + ][ - + ]: 1792 : HMAP_FOR_EACH_WITH_HASH(pr_op, hmap_node, hash, &bond->pr_rule_ops) {
303 [ # # ]: 0 : if (match_equal(&pr_op->match, match)) {
304 : 0 : pr_op->op = ADD;
305 : 0 : pr_op->out_ofport = out_ofport;
306 : 0 : pr_op->pr_rule = rule;
307 : 0 : return;
308 : : }
309 : : }
310 : :
311 : 1792 : pr_op = xmalloc(sizeof *pr_op);
312 : 1792 : pr_op->match = *match;
313 : 1792 : pr_op->op = ADD;
314 : 1792 : pr_op->out_ofport = out_ofport;
315 : 1792 : pr_op->pr_rule = rule;
316 : 1792 : hmap_insert(&bond->pr_rule_ops, &pr_op->hmap_node, hash);
317 : : }
318 : :
319 : : static void
320 : 31 : update_recirc_rules(struct bond *bond)
321 : : OVS_REQ_WRLOCK(rwlock)
322 : : {
323 : : struct match match;
324 : : struct bond_pr_rule_op *pr_op, *next_op;
325 : : uint64_t ofpacts_stub[128 / 8];
326 : : struct ofpbuf ofpacts;
327 : : int i;
328 : :
329 : 31 : ofpbuf_use_stub(&ofpacts, ofpacts_stub, sizeof ofpacts_stub);
330 : :
331 [ + + ][ - + ]: 543 : HMAP_FOR_EACH(pr_op, hmap_node, &bond->pr_rule_ops) {
332 : 512 : pr_op->op = DEL;
333 : : }
334 : :
335 [ + - ][ + - ]: 31 : if (bond->hash && bond->recirc_id) {
336 [ + + ]: 7967 : for (i = 0; i < BOND_BUCKETS; i++) {
337 : 7936 : struct bond_slave *slave = bond->hash[i].slave;
338 : :
339 [ + + ]: 7936 : if (slave) {
340 : 1792 : match_init_catchall(&match);
341 : 1792 : match_set_recirc_id(&match, bond->recirc_id);
342 : 1792 : match_set_dp_hash_masked(&match, i, BOND_MASK);
343 : :
344 : 1792 : add_pr_rule(bond, &match, slave->ofp_port,
345 : 1792 : &bond->hash[i].pr_rule);
346 : : }
347 : : }
348 : : }
349 : :
350 [ + + ][ - + ]: 2335 : HMAP_FOR_EACH_SAFE(pr_op, next_op, hmap_node, &bond->pr_rule_ops) {
[ + + ]
351 : : int error;
352 [ + + - ]: 2304 : switch (pr_op->op) {
353 : : case ADD:
354 : 1792 : ofpbuf_clear(&ofpacts);
355 : 1792 : ofpact_put_OUTPUT(&ofpacts)->port = pr_op->out_ofport;
356 : 1792 : error = ofproto_dpif_add_internal_flow(bond->ofproto,
357 : 1792 : &pr_op->match,
358 : : RECIRC_RULE_PRIORITY, 0,
359 : : &ofpacts, pr_op->pr_rule);
360 [ - + ]: 1792 : if (error) {
361 : 0 : char *err_s = match_to_string(&pr_op->match,
362 : : RECIRC_RULE_PRIORITY);
363 : :
364 [ # # ]: 0 : VLOG_ERR("failed to add post recirculation flow %s", err_s);
365 : 0 : free(err_s);
366 : : }
367 : 1792 : break;
368 : :
369 : : case DEL:
370 : 512 : error = ofproto_dpif_delete_internal_flow(bond->ofproto,
371 : : &pr_op->match,
372 : : RECIRC_RULE_PRIORITY);
373 [ - + ]: 512 : if (error) {
374 : 0 : char *err_s = match_to_string(&pr_op->match,
375 : : RECIRC_RULE_PRIORITY);
376 : :
377 [ # # ]: 0 : VLOG_ERR("failed to remove post recirculation flow %s", err_s);
378 : 0 : free(err_s);
379 : : }
380 : :
381 : 512 : hmap_remove(&bond->pr_rule_ops, &pr_op->hmap_node);
382 : 512 : *pr_op->pr_rule = NULL;
383 : 512 : free(pr_op);
384 : 512 : break;
385 : : }
386 : : }
387 : :
388 : 31 : ofpbuf_uninit(&ofpacts);
389 : 31 : }
390 : :
391 : :
392 : : /* Updates 'bond''s overall configuration to 's'.
393 : : *
394 : : * The caller should register each slave on 'bond' by calling
395 : : * bond_slave_register(). This is optional if none of the slaves'
396 : : * configuration has changed. In any case it can't hurt.
397 : : *
398 : : * Returns true if the configuration has changed in such a way that requires
399 : : * flow revalidation.
400 : : * */
401 : : bool
402 : 32 : bond_reconfigure(struct bond *bond, const struct bond_settings *s)
403 : : {
404 : 32 : bool revalidate = false;
405 : :
406 : 32 : ovs_rwlock_wrlock(&rwlock);
407 [ + + ][ - + ]: 32 : if (!bond->name || strcmp(bond->name, s->name)) {
408 [ - + ]: 19 : if (bond->name) {
409 : 0 : hmap_remove(all_bonds, &bond->hmap_node);
410 : 0 : free(bond->name);
411 : : }
412 : 19 : bond->name = xstrdup(s->name);
413 : 19 : hmap_insert(all_bonds, &bond->hmap_node, hash_string(bond->name, 0));
414 : : }
415 : :
416 : 32 : bond->updelay = s->up_delay;
417 : 32 : bond->downdelay = s->down_delay;
418 : :
419 [ - + ]: 32 : if (bond->lacp_fallback_ab != s->lacp_fallback_ab_cfg) {
420 : 0 : bond->lacp_fallback_ab = s->lacp_fallback_ab_cfg;
421 : 0 : revalidate = true;
422 : : }
423 : :
424 [ + + ]: 32 : if (bond->rebalance_interval != s->rebalance_interval) {
425 : 11 : bond->rebalance_interval = s->rebalance_interval;
426 : 11 : revalidate = true;
427 : : }
428 : :
429 [ + + ]: 32 : if (bond->balance != s->balance) {
430 : 9 : bond->balance = s->balance;
431 : 9 : revalidate = true;
432 : : }
433 : :
434 [ - + ]: 32 : if (bond->basis != s->basis) {
435 : 0 : bond->basis = s->basis;
436 : 0 : revalidate = true;
437 : : }
438 : :
439 [ - + ]: 32 : if (bond->bond_revalidate) {
440 : 0 : revalidate = true;
441 : 0 : bond->bond_revalidate = false;
442 : : }
443 : :
444 [ + + ]: 32 : if (bond->balance != BM_AB) {
445 [ + + ]: 18 : if (!bond->recirc_id) {
446 : 18 : bond->recirc_id = recirc_alloc_id(bond->ofproto);
447 : : }
448 [ - + ]: 14 : } else if (bond->recirc_id) {
449 : 0 : recirc_free_id(bond->recirc_id);
450 : 0 : bond->recirc_id = 0;
451 : : }
452 : :
453 [ + + ][ + + ]: 32 : if (bond->balance == BM_AB || !bond->hash || revalidate) {
[ - + ]
454 : 26 : bond_entry_reset(bond);
455 : : }
456 : :
457 : 32 : bond->active_slave_mac = s->active_slave_mac;
458 : 32 : bond->active_slave_changed = false;
459 : :
460 : 32 : ovs_rwlock_unlock(&rwlock);
461 : 32 : return revalidate;
462 : : }
463 : :
464 : : static struct bond_slave *
465 : 418 : bond_find_slave_by_mac(const struct bond *bond, const struct eth_addr mac)
466 : : {
467 : : struct bond_slave *slave;
468 : :
469 : : /* Find the last active slave */
470 [ + + ][ - + ]: 1223 : HMAP_FOR_EACH(slave, hmap_node, &bond->slaves) {
471 : : struct eth_addr slave_mac;
472 : :
473 [ - + ]: 823 : if (netdev_get_etheraddr(slave->netdev, &slave_mac)) {
474 : 0 : continue;
475 : : }
476 : :
477 [ + + ]: 823 : if (eth_addr_equals(slave_mac, mac)) {
478 : 823 : return slave;
479 : : }
480 : : }
481 : :
482 : 400 : return NULL;
483 : : }
484 : :
485 : : static void
486 : 27 : bond_active_slave_changed(struct bond *bond)
487 : : {
488 : : struct eth_addr mac;
489 : :
490 : 27 : netdev_get_etheraddr(bond->active_slave->netdev, &mac);
491 : 27 : bond->active_slave_mac = mac;
492 : 27 : bond->active_slave_changed = true;
493 : 27 : seq_change(connectivity_seq_get());
494 : 27 : }
495 : :
496 : : static void
497 : 103 : bond_slave_set_netdev__(struct bond_slave *slave, struct netdev *netdev)
498 : : OVS_REQ_WRLOCK(rwlock)
499 : : {
500 [ + + ]: 103 : if (slave->netdev != netdev) {
501 : 41 : slave->netdev = netdev;
502 : 41 : slave->change_seq = 0;
503 : : }
504 : 103 : }
505 : :
506 : : /* Registers 'slave_' as a slave of 'bond'. The 'slave_' pointer is an
507 : : * arbitrary client-provided pointer that uniquely identifies a slave within a
508 : : * bond. If 'slave_' already exists within 'bond' then this function
509 : : * reconfigures the existing slave.
510 : : *
511 : : * 'netdev' must be the network device that 'slave_' represents. It is owned
512 : : * by the client, so the client must not close it before either unregistering
513 : : * 'slave_' or destroying 'bond'.
514 : : */
515 : : void
516 : 67 : bond_slave_register(struct bond *bond, void *slave_,
517 : : ofp_port_t ofport, struct netdev *netdev)
518 : : {
519 : : struct bond_slave *slave;
520 : :
521 : 67 : ovs_rwlock_wrlock(&rwlock);
522 : 67 : slave = bond_slave_lookup(bond, slave_);
523 [ + + ]: 67 : if (!slave) {
524 : 41 : slave = xzalloc(sizeof *slave);
525 : :
526 : 41 : hmap_insert(&bond->slaves, &slave->hmap_node, hash_pointer(slave_, 0));
527 : 41 : slave->bond = bond;
528 : 41 : slave->aux = slave_;
529 : 41 : slave->ofp_port = ofport;
530 : 41 : slave->delay_expires = LLONG_MAX;
531 : 41 : slave->name = xstrdup(netdev_get_name(netdev));
532 : 41 : bond->bond_revalidate = true;
533 : :
534 : 41 : slave->enabled = false;
535 : 41 : bond_enable_slave(slave, netdev_get_carrier(netdev));
536 : : }
537 : :
538 : 67 : bond_slave_set_netdev__(slave, netdev);
539 : :
540 : 67 : free(slave->name);
541 : 67 : slave->name = xstrdup(netdev_get_name(netdev));
542 : 67 : ovs_rwlock_unlock(&rwlock);
543 : 67 : }
544 : :
545 : : /* Updates the network device to be used with 'slave_' to 'netdev'.
546 : : *
547 : : * This is useful if the caller closes and re-opens the network device
548 : : * registered with bond_slave_register() but doesn't need to change anything
549 : : * else. */
550 : : void
551 : 36 : bond_slave_set_netdev(struct bond *bond, void *slave_, struct netdev *netdev)
552 : : {
553 : : struct bond_slave *slave;
554 : :
555 : 36 : ovs_rwlock_wrlock(&rwlock);
556 : 36 : slave = bond_slave_lookup(bond, slave_);
557 [ + - ]: 36 : if (slave) {
558 : 36 : bond_slave_set_netdev__(slave, netdev);
559 : : }
560 : 36 : ovs_rwlock_unlock(&rwlock);
561 : 36 : }
562 : :
563 : : /* Unregisters 'slave_' from 'bond'. If 'bond' does not contain such a slave
564 : : * then this function has no effect.
565 : : *
566 : : * Unregistering a slave invalidates all flows. */
567 : : void
568 : 41 : bond_slave_unregister(struct bond *bond, const void *slave_)
569 : : {
570 : : struct bond_slave *slave;
571 : : bool del_active;
572 : :
573 : 41 : ovs_rwlock_wrlock(&rwlock);
574 : 41 : slave = bond_slave_lookup(bond, slave_);
575 [ - + ]: 41 : if (!slave) {
576 : 0 : goto out;
577 : : }
578 : :
579 : 41 : bond->bond_revalidate = true;
580 : 41 : bond_enable_slave(slave, false);
581 : :
582 : 41 : del_active = bond->active_slave == slave;
583 [ + + ]: 41 : if (bond->hash) {
584 : : struct bond_entry *e;
585 [ + + ]: 6939 : for (e = bond->hash; e <= &bond->hash[BOND_MASK]; e++) {
586 [ + + ]: 6912 : if (e->slave == slave) {
587 : 1365 : e->slave = NULL;
588 : : }
589 : : }
590 : : }
591 : :
592 : 41 : free(slave->name);
593 : :
594 : 41 : hmap_remove(&bond->slaves, &slave->hmap_node);
595 : : /* Client owns 'slave->netdev'. */
596 : 41 : free(slave);
597 : :
598 [ + + ]: 41 : if (del_active) {
599 : 26 : bond_choose_active_slave(bond);
600 : 26 : bond->send_learning_packets = true;
601 : : }
602 : : out:
603 : 41 : ovs_rwlock_unlock(&rwlock);
604 : 41 : }
605 : :
606 : : /* Should be called on each slave in 'bond' before bond_run() to indicate
607 : : * whether or not 'slave_' may be enabled. This function is intended to allow
608 : : * other protocols to have some impact on bonding decisions. For example LACP
609 : : * or high level link monitoring protocols may decide that a given slave should
610 : : * not be able to send traffic. */
611 : : void
612 : 14728 : bond_slave_set_may_enable(struct bond *bond, void *slave_, bool may_enable)
613 : : {
614 : 14728 : ovs_rwlock_wrlock(&rwlock);
615 : 14728 : bond_slave_lookup(bond, slave_)->may_enable = may_enable;
616 : 14728 : ovs_rwlock_unlock(&rwlock);
617 : 14728 : }
618 : :
619 : : /* Performs periodic maintenance on 'bond'.
620 : : *
621 : : * Returns true if the caller should revalidate its flows.
622 : : *
623 : : * The caller should check bond_should_send_learning_packets() afterward. */
624 : : bool
625 : 5410 : bond_run(struct bond *bond, enum lacp_status lacp_status)
626 : : {
627 : : struct bond_slave *slave;
628 : : bool revalidate;
629 : :
630 : 5410 : ovs_rwlock_wrlock(&rwlock);
631 [ + + ]: 5410 : if (bond->lacp_status != lacp_status) {
632 : 7 : bond->lacp_status = lacp_status;
633 : 7 : bond->bond_revalidate = true;
634 : : }
635 : :
636 : : /* Enable slaves based on link status and LACP feedback. */
637 [ + + ][ - + ]: 20138 : HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
638 : 14728 : bond_link_status_update(slave);
639 : 14728 : slave->change_seq = seq_read(connectivity_seq_get());
640 : : }
641 [ + + ][ + + ]: 5410 : if (!bond->active_slave || !bond->active_slave->enabled) {
642 : 374 : bond_choose_active_slave(bond);
643 : : }
644 : :
645 : 5410 : revalidate = bond->bond_revalidate;
646 : 5410 : bond->bond_revalidate = false;
647 : 5410 : ovs_rwlock_unlock(&rwlock);
648 : :
649 : 5410 : return revalidate;
650 : : }
651 : :
652 : : /* Causes poll_block() to wake up when 'bond' needs something to be done. */
653 : : void
654 : 5397 : bond_wait(struct bond *bond)
655 : : {
656 : : struct bond_slave *slave;
657 : :
658 : 5397 : ovs_rwlock_rdlock(&rwlock);
659 [ + + ][ - + ]: 20099 : HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
660 [ - + ]: 14702 : if (slave->delay_expires != LLONG_MAX) {
661 : 0 : poll_timer_wait_until(slave->delay_expires);
662 : : }
663 : :
664 : 14702 : seq_wait(connectivity_seq_get(), slave->change_seq);
665 : : }
666 : :
667 [ - + ]: 5397 : if (bond->bond_revalidate) {
668 : 0 : poll_immediate_wake();
669 : : }
670 : 5397 : ovs_rwlock_unlock(&rwlock);
671 : :
672 : : /* We don't wait for bond->next_rebalance because rebalancing can only run
673 : : * at a flow account checkpoint. ofproto does checkpointing on its own
674 : : * schedule and bond_rebalance() gets called afterward, so we'd just be
675 : : * waking up for no purpose. */
676 : 5397 : }
677 : :
678 : : /* MAC learning table interaction. */
679 : :
680 : : static bool
681 : 20 : may_send_learning_packets(const struct bond *bond)
682 : : {
683 : 20 : return ((bond->lacp_status == LACP_DISABLED
684 [ + + ][ - + ]: 10 : && (bond->balance == BM_SLB || bond->balance == BM_AB))
685 [ - + ][ # # ]: 10 : || (bond->lacp_fallback_ab && bond->lacp_status == LACP_CONFIGURED))
686 [ + + ][ + - ]: 40 : && bond->active_slave;
687 : : }
688 : :
689 : : /* Returns true if 'bond' needs the client to send out packets to assist with
690 : : * MAC learning on 'bond'. If this function returns true, then the client
691 : : * should iterate through its MAC learning table for the bridge on which 'bond'
692 : : * is located. For each MAC that has been learned on a port other than 'bond',
693 : : * it should call bond_compose_learning_packet().
694 : : *
695 : : * This function will only return true if 'bond' is in SLB or active-backup
696 : : * mode and LACP is not negotiated. Otherwise sending learning packets isn't
697 : : * necessary.
698 : : *
699 : : * Calling this function resets the state that it checks. */
700 : : bool
701 : 5410 : bond_should_send_learning_packets(struct bond *bond)
702 : : {
703 : : bool send;
704 : :
705 : 5410 : ovs_rwlock_wrlock(&rwlock);
706 [ + + ][ + + ]: 5410 : send = bond->send_learning_packets && may_send_learning_packets(bond);
707 : 5410 : bond->send_learning_packets = false;
708 : 5410 : ovs_rwlock_unlock(&rwlock);
709 : 5410 : return send;
710 : : }
711 : :
712 : : /* Sends a gratuitous learning packet on 'bond' from 'eth_src' on 'vlan'.
713 : : *
714 : : * See bond_should_send_learning_packets() for description of usage. The
715 : : * caller should send the composed packet on the port associated with
716 : : * port_aux and takes ownership of the returned ofpbuf. */
717 : : struct dp_packet *
718 : 2 : bond_compose_learning_packet(struct bond *bond, const struct eth_addr eth_src,
719 : : uint16_t vlan, void **port_aux)
720 : : {
721 : : struct bond_slave *slave;
722 : : struct dp_packet *packet;
723 : : struct flow flow;
724 : :
725 : 2 : ovs_rwlock_rdlock(&rwlock);
726 [ - + ]: 2 : ovs_assert(may_send_learning_packets(bond));
727 : 2 : memset(&flow, 0, sizeof flow);
728 : 2 : flow.dl_src = eth_src;
729 : 2 : slave = choose_output_slave(bond, &flow, NULL, vlan);
730 : :
731 : 2 : packet = dp_packet_new(0);
732 : 2 : compose_rarp(packet, eth_src);
733 [ - + ]: 2 : if (vlan) {
734 : 0 : eth_push_vlan(packet, htons(ETH_TYPE_VLAN), htons(vlan));
735 : : }
736 : :
737 : 2 : *port_aux = slave->aux;
738 : 2 : ovs_rwlock_unlock(&rwlock);
739 : 2 : return packet;
740 : : }
741 : :
742 : : /* Checks whether a packet that arrived on 'slave_' within 'bond', with an
743 : : * Ethernet destination address of 'eth_dst', should be admitted.
744 : : *
745 : : * The return value is one of the following:
746 : : *
747 : : * - BV_ACCEPT: Admit the packet.
748 : : *
749 : : * - BV_DROP: Drop the packet.
750 : : *
751 : : * - BV_DROP_IF_MOVED: Consult the MAC learning table for the packet's
752 : : * Ethernet source address and VLAN. If there is none, or if the packet
753 : : * is on the learned port, then admit the packet. If a different port has
754 : : * been learned, however, drop the packet (and do not use it for MAC
755 : : * learning).
756 : : */
757 : : enum bond_verdict
758 : 12363 : bond_check_admissibility(struct bond *bond, const void *slave_,
759 : : const struct eth_addr eth_dst)
760 : : {
761 : 12363 : enum bond_verdict verdict = BV_DROP;
762 : : struct bond_slave *slave;
763 : :
764 : 12363 : ovs_rwlock_rdlock(&rwlock);
765 : 12363 : slave = bond_slave_lookup(bond, slave_);
766 [ - + ]: 12363 : if (!slave) {
767 : 0 : goto out;
768 : : }
769 : :
770 : : /* LACP bonds have very loose admissibility restrictions because we can
771 : : * assume the remote switch is aware of the bond and will "do the right
772 : : * thing". However, as a precaution we drop packets on disabled slaves
773 : : * because no correctly implemented partner switch should be sending
774 : : * packets to them.
775 : : *
776 : : * If LACP is configured, but LACP negotiations have been unsuccessful, we
777 : : * drop all incoming traffic except if lacp_fallback_ab is enabled. */
778 [ + - + - ]: 12363 : switch (bond->lacp_status) {
779 : : case LACP_NEGOTIATED:
780 [ + + ]: 312 : verdict = slave->enabled ? BV_ACCEPT : BV_DROP;
781 : 312 : goto out;
782 : : case LACP_CONFIGURED:
783 [ # # ]: 0 : if (!bond->lacp_fallback_ab) {
784 : 0 : goto out;
785 : : }
786 : : case LACP_DISABLED:
787 : 12051 : break;
788 : : }
789 : :
790 : : /* Drop all multicast packets on inactive slaves. */
791 [ + + ]: 12051 : if (eth_addr_is_multicast(eth_dst)) {
792 [ + - ]: 18 : if (bond->active_slave != slave) {
793 : 18 : goto out;
794 : : }
795 : : }
796 : :
797 [ - + + - ]: 12033 : switch (bond->balance) {
798 : : case BM_TCP:
799 : : /* TCP balanced bonds require successful LACP negotiations. Based on the
800 : : * above check, LACP is off or lacp_fallback_ab is true on this bond.
801 : : * If lacp_fallback_ab is true fall through to BM_AB case else, we
802 : : * drop all incoming traffic. */
803 [ # # ]: 0 : if (!bond->lacp_fallback_ab) {
804 : 0 : goto out;
805 : : }
806 : :
807 : : case BM_AB:
808 : : /* Drop all packets which arrive on backup slaves. This is similar to
809 : : * how Linux bonding handles active-backup bonds. */
810 [ + + ]: 19 : if (bond->active_slave != slave) {
811 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(1, 5);
812 : :
813 [ - + ]: 13 : VLOG_DBG_RL(&rl, "active-backup bond received packet on backup"
814 : : " slave (%s) destined for " ETH_ADDR_FMT,
815 : : slave->name, ETH_ADDR_ARGS(eth_dst));
816 : 13 : goto out;
817 : : }
818 : 6 : verdict = BV_ACCEPT;
819 : 6 : goto out;
820 : :
821 : : case BM_SLB:
822 : : /* Drop all packets for which we have learned a different input port,
823 : : * because we probably sent the packet on one slave and got it back on
824 : : * the other. Gratuitous ARP packets are an exception to this rule:
825 : : * the host has moved to another switch. The exception to the
826 : : * exception is if we locked the learning table to avoid reflections on
827 : : * bond slaves. */
828 : 12014 : verdict = BV_DROP_IF_MOVED;
829 : 12014 : goto out;
830 : : }
831 : :
832 : 0 : OVS_NOT_REACHED();
833 : : out:
834 : 12363 : ovs_rwlock_unlock(&rwlock);
835 : 12363 : return verdict;
836 : :
837 : : }
838 : :
839 : : /* Returns the slave (registered on 'bond' by bond_slave_register()) to which
840 : : * a packet with the given 'flow' and 'vlan' should be forwarded. Returns
841 : : * NULL if the packet should be dropped because no slaves are enabled.
842 : : *
843 : : * 'vlan' is not necessarily the same as 'flow->vlan_tci'. First, 'vlan'
844 : : * should be a VID only (i.e. excluding the PCP bits). Second,
845 : : * 'flow->vlan_tci' is the VLAN TCI that appeared on the packet (so it will be
846 : : * nonzero only for trunk ports), whereas 'vlan' is the logical VLAN that the
847 : : * packet belongs to (so for an access port it will be the access port's VLAN).
848 : : *
849 : : * If 'wc' is non-NULL, bitwise-OR's 'wc' with the set of bits that were
850 : : * significant in the selection. At some point earlier, 'wc' should
851 : : * have been initialized (e.g., by flow_wildcards_init_catchall()).
852 : : */
853 : : void *
854 : 6420 : bond_choose_output_slave(struct bond *bond, const struct flow *flow,
855 : : struct flow_wildcards *wc, uint16_t vlan)
856 : : {
857 : : struct bond_slave *slave;
858 : : void *aux;
859 : :
860 : 6420 : ovs_rwlock_rdlock(&rwlock);
861 : 6420 : slave = choose_output_slave(bond, flow, wc, vlan);
862 [ + + ]: 6420 : aux = slave ? slave->aux : NULL;
863 : 6420 : ovs_rwlock_unlock(&rwlock);
864 : :
865 : 6420 : return aux;
866 : : }
867 : :
868 : : /* Recirculation. */
869 : : static void
870 : 0 : bond_entry_account(struct bond_entry *entry, uint64_t rule_tx_bytes)
871 : : OVS_REQ_WRLOCK(rwlock)
872 : : {
873 [ # # ]: 0 : if (entry->slave) {
874 : : uint64_t delta;
875 : :
876 : 0 : delta = rule_tx_bytes - entry->pr_tx_bytes;
877 : 0 : entry->tx_bytes += delta;
878 : 0 : entry->pr_tx_bytes = rule_tx_bytes;
879 : : }
880 : 0 : }
881 : :
882 : : /* Maintain bond stats using post recirculation rule byte counters.*/
883 : : static void
884 : 6 : bond_recirculation_account(struct bond *bond)
885 : : OVS_REQ_WRLOCK(rwlock)
886 : : {
887 : : int i;
888 : :
889 [ + + ]: 1542 : for (i=0; i<=BOND_MASK; i++) {
890 : 1536 : struct bond_entry *entry = &bond->hash[i];
891 : 1536 : struct rule *rule = entry->pr_rule;
892 : :
893 [ - + ]: 1536 : if (rule) {
894 : : uint64_t n_packets OVS_UNUSED;
895 : : long long int used OVS_UNUSED;
896 : : uint64_t n_bytes;
897 : :
898 : 0 : rule->ofproto->ofproto_class->rule_get_stats(
899 : : rule, &n_packets, &n_bytes, &used);
900 : 0 : bond_entry_account(entry, n_bytes);
901 : : }
902 : : }
903 : 6 : }
904 : :
905 : : bool
906 : 6444 : bond_may_recirc(const struct bond *bond, uint32_t *recirc_id,
907 : : uint32_t *hash_bias)
908 : : {
909 [ + + ][ + - ]: 6444 : if (bond->balance == BM_TCP && bond->recirc_id) {
910 [ + + ]: 327 : if (recirc_id) {
911 : 321 : *recirc_id = bond->recirc_id;
912 : : }
913 [ + + ]: 327 : if (hash_bias) {
914 : 309 : *hash_bias = bond->basis;
915 : : }
916 : 327 : return true;
917 : : } else {
918 : 6117 : return false;
919 : : }
920 : : }
921 : :
922 : : static void
923 : 309 : bond_update_post_recirc_rules__(struct bond* bond, const bool force)
924 : : OVS_REQ_WRLOCK(rwlock)
925 : : {
926 : : struct bond_entry *e;
927 : 309 : bool update_rules = force; /* Always update rules if caller forces it. */
928 : :
929 : : /* Make sure all bond entries are populated */
930 [ + + ]: 79413 : for (e = bond->hash; e <= &bond->hash[BOND_MASK]; e++) {
931 [ + + ][ + + ]: 79104 : if (!e->slave || !e->slave->enabled) {
932 : 7936 : update_rules = true;
933 : 7936 : e->slave = CONTAINER_OF(hmap_random_node(&bond->slaves),
934 : : struct bond_slave, hmap_node);
935 [ + + ]: 7936 : if (!e->slave->enabled) {
936 : 6144 : e->slave = bond->active_slave;
937 : : }
938 : : }
939 : : }
940 : :
941 [ + + ]: 309 : if (update_rules) {
942 : 31 : update_recirc_rules(bond);
943 : : }
944 : 309 : }
945 : :
946 : : void
947 : 309 : bond_update_post_recirc_rules(struct bond* bond, const bool force)
948 : : {
949 : 309 : ovs_rwlock_wrlock(&rwlock);
950 : 309 : bond_update_post_recirc_rules__(bond, force);
951 : 309 : ovs_rwlock_unlock(&rwlock);
952 : 309 : }
953 : :
954 : : /* Rebalancing. */
955 : :
956 : : static bool
957 : 1232 : bond_is_balanced(const struct bond *bond) OVS_REQ_RDLOCK(rwlock)
958 : : {
959 : 1232 : return bond->rebalance_interval
960 [ + + ][ + + ]: 1232 : && (bond->balance == BM_SLB || bond->balance == BM_TCP);
[ + + ]
961 : : }
962 : :
963 : : /* Notifies 'bond' that 'n_bytes' bytes were sent in 'flow' within 'vlan'. */
964 : : void
965 : 111 : bond_account(struct bond *bond, const struct flow *flow, uint16_t vlan,
966 : : uint64_t n_bytes)
967 : : {
968 : 111 : ovs_rwlock_wrlock(&rwlock);
969 [ + + ]: 111 : if (bond_is_balanced(bond)) {
970 : 103 : lookup_bond_entry(bond, flow, vlan)->tx_bytes += n_bytes;
971 : : }
972 : 111 : ovs_rwlock_unlock(&rwlock);
973 : 111 : }
974 : :
975 : : static struct bond_slave *
976 : 2 : bond_slave_from_bal_node(struct ovs_list *bal) OVS_REQ_RDLOCK(rwlock)
977 : : {
978 : 2 : return CONTAINER_OF(bal, struct bond_slave, bal_node);
979 : : }
980 : :
981 : : static void
982 : 6 : log_bals(struct bond *bond, const struct ovs_list *bals)
983 : : OVS_REQ_RDLOCK(rwlock)
984 : : {
985 [ - + ]: 6 : if (VLOG_IS_DBG_ENABLED()) {
986 : 0 : struct ds ds = DS_EMPTY_INITIALIZER;
987 : : const struct bond_slave *slave;
988 : :
989 [ # # ]: 0 : LIST_FOR_EACH (slave, bal_node, bals) {
990 [ # # ]: 0 : if (ds.length) {
991 : 0 : ds_put_char(&ds, ',');
992 : : }
993 : 0 : ds_put_format(&ds, " %s %"PRIu64"kB",
994 : 0 : slave->name, slave->tx_bytes / 1024);
995 : :
996 [ # # ]: 0 : if (!slave->enabled) {
997 : 0 : ds_put_cstr(&ds, " (disabled)");
998 : : }
999 [ # # ]: 0 : if (!ovs_list_is_empty(&slave->entries)) {
1000 : : struct bond_entry *e;
1001 : :
1002 : 0 : ds_put_cstr(&ds, " (");
1003 [ # # ]: 0 : LIST_FOR_EACH (e, list_node, &slave->entries) {
1004 [ # # ]: 0 : if (&e->list_node != ovs_list_front(&slave->entries)) {
1005 : 0 : ds_put_cstr(&ds, " + ");
1006 : : }
1007 : 0 : ds_put_format(&ds, "h%"PRIdPTR": %"PRIu64"kB",
1008 : 0 : e - bond->hash, e->tx_bytes / 1024);
1009 : : }
1010 : 0 : ds_put_cstr(&ds, ")");
1011 : : }
1012 : : }
1013 [ # # ]: 0 : VLOG_DBG("bond %s:%s", bond->name, ds_cstr(&ds));
1014 : 0 : ds_destroy(&ds);
1015 : : }
1016 : 6 : }
1017 : :
1018 : : /* Shifts 'hash' from its current slave to 'to'. */
1019 : : static void
1020 : 0 : bond_shift_load(struct bond_entry *hash, struct bond_slave *to)
1021 : : OVS_REQ_WRLOCK(rwlock)
1022 : : {
1023 : 0 : struct bond_slave *from = hash->slave;
1024 : 0 : struct bond *bond = from->bond;
1025 : 0 : uint64_t delta = hash->tx_bytes;
1026 : :
1027 [ # # ]: 0 : VLOG_INFO("bond %s: shift %"PRIu64"kB of load (with hash %"PRIdPTR") "
1028 : : "from %s to %s (now carrying %"PRIu64"kB and "
1029 : : "%"PRIu64"kB load, respectively)",
1030 : : bond->name, delta / 1024, hash - bond->hash,
1031 : : from->name, to->name,
1032 : : (from->tx_bytes - delta) / 1024,
1033 : : (to->tx_bytes + delta) / 1024);
1034 : :
1035 : : /* Shift load away from 'from' to 'to'. */
1036 : 0 : from->tx_bytes -= delta;
1037 : 0 : to->tx_bytes += delta;
1038 : :
1039 : : /* Arrange for flows to be revalidated. */
1040 : 0 : hash->slave = to;
1041 : 0 : bond->bond_revalidate = true;
1042 : 0 : }
1043 : :
1044 : : /* Picks and returns a bond_entry to migrate from 'from' (the most heavily
1045 : : * loaded bond slave) to a bond slave that has 'to_tx_bytes' bytes of load,
1046 : : * given that doing so must decrease the ratio of the load on the two slaves by
1047 : : * at least 0.1. Returns NULL if there is no appropriate entry.
1048 : : *
1049 : : * The list of entries isn't sorted. I don't know of a reason to prefer to
1050 : : * shift away small hashes or large hashes. */
1051 : : static struct bond_entry *
1052 : 0 : choose_entry_to_migrate(const struct bond_slave *from, uint64_t to_tx_bytes)
1053 : : OVS_REQ_WRLOCK(rwlock)
1054 : : {
1055 : : struct bond_entry *e;
1056 : :
1057 [ # # ]: 0 : if (ovs_list_is_short(&from->entries)) {
1058 : : /* 'from' carries no more than one MAC hash, so shifting load away from
1059 : : * it would be pointless. */
1060 : 0 : return NULL;
1061 : : }
1062 : :
1063 [ # # ]: 0 : LIST_FOR_EACH (e, list_node, &from->entries) {
1064 : 0 : uint64_t delta = e->tx_bytes; /* The amount to rebalance. */
1065 : 0 : uint64_t ideal_tx_bytes = (from->tx_bytes + to_tx_bytes)/2;
1066 : : /* Note, the ideal traffic is the mid point
1067 : : * between 'from' and 'to'. This value does
1068 : : * not change by rebalancing. */
1069 : : uint64_t new_low; /* The lower bandwidth between 'to' and 'from'
1070 : : after rebalancing. */
1071 : :
1072 : 0 : new_low = MIN(from->tx_bytes - delta, to_tx_bytes + delta);
1073 : :
1074 [ # # ][ # # ]: 0 : if ((new_low > to_tx_bytes) &&
1075 : 0 : (new_low - to_tx_bytes >= (ideal_tx_bytes - to_tx_bytes) / 10)) {
1076 : : /* Only rebalance if the new 'low' is closer to to the mid point,
1077 : : * and the improvement exceeds 10% of current traffic
1078 : : * deviation from the ideal split.
1079 : : *
1080 : : * The improvement on the 'high' side is always the same as the
1081 : : * 'low' side. Thus consider 'low' side is sufficient. */
1082 : 0 : return e;
1083 : : }
1084 : : }
1085 : :
1086 : 0 : return NULL;
1087 : : }
1088 : :
1089 : : /* Inserts 'slave' into 'bals' so that descending order of 'tx_bytes' is
1090 : : * maintained. */
1091 : : static void
1092 : 2 : insert_bal(struct ovs_list *bals, struct bond_slave *slave)
1093 : : {
1094 : : struct bond_slave *pos;
1095 : :
1096 [ + + ]: 3 : LIST_FOR_EACH (pos, bal_node, bals) {
1097 [ - + ]: 1 : if (slave->tx_bytes > pos->tx_bytes) {
1098 : 0 : break;
1099 : : }
1100 : : }
1101 : 2 : ovs_list_insert(&pos->bal_node, &slave->bal_node);
1102 : 2 : }
1103 : :
1104 : : /* Removes 'slave' from its current list and then inserts it into 'bals' so
1105 : : * that descending order of 'tx_bytes' is maintained. */
1106 : : static void
1107 : 0 : reinsert_bal(struct ovs_list *bals, struct bond_slave *slave)
1108 : : {
1109 : 0 : ovs_list_remove(&slave->bal_node);
1110 : 0 : insert_bal(bals, slave);
1111 : 0 : }
1112 : :
1113 : : /* If 'bond' needs rebalancing, does so.
1114 : : *
1115 : : * The caller should have called bond_account() for each active flow, or in case
1116 : : * of recirculation is used, have called bond_recirculation_account(bond),
1117 : : * to ensure that flow data is consistently accounted at this point.
1118 : : */
1119 : : void
1120 : 1063 : bond_rebalance(struct bond *bond)
1121 : : {
1122 : : struct bond_slave *slave;
1123 : : struct bond_entry *e;
1124 : : struct ovs_list bals;
1125 : 1063 : bool rebalanced = false;
1126 : : bool use_recirc;
1127 : :
1128 : 1063 : ovs_rwlock_wrlock(&rwlock);
1129 [ + + ][ + + ]: 1063 : if (!bond_is_balanced(bond) || time_msec() < bond->next_rebalance) {
1130 : : goto done;
1131 : : }
1132 : 6 : bond->next_rebalance = time_msec() + bond->rebalance_interval;
1133 : :
1134 [ + - + - ]: 12 : use_recirc = ofproto_dpif_get_support(bond->ofproto)->odp.recirc &&
1135 : 6 : bond_may_recirc(bond, NULL, NULL);
1136 : :
1137 [ + - ]: 6 : if (use_recirc) {
1138 : 6 : bond_recirculation_account(bond);
1139 : : }
1140 : :
1141 : : /* Add each bond_entry to its slave's 'entries' list.
1142 : : * Compute each slave's tx_bytes as the sum of its entries' tx_bytes. */
1143 [ + + ][ - + ]: 18 : HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
1144 : 12 : slave->tx_bytes = 0;
1145 : 12 : ovs_list_init(&slave->entries);
1146 : : }
1147 [ + + ]: 1542 : for (e = &bond->hash[0]; e <= &bond->hash[BOND_MASK]; e++) {
1148 [ - + ][ # # ]: 1536 : if (e->slave && e->tx_bytes) {
1149 : 0 : e->slave->tx_bytes += e->tx_bytes;
1150 : 0 : ovs_list_push_back(&e->slave->entries, &e->list_node);
1151 : : }
1152 : : }
1153 : :
1154 : : /* Add enabled slaves to 'bals' in descending order of tx_bytes.
1155 : : *
1156 : : * XXX This is O(n**2) in the number of slaves but it could be O(n lg n)
1157 : : * with a proper list sort algorithm. */
1158 : 6 : ovs_list_init(&bals);
1159 [ + + ][ - + ]: 18 : HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
1160 [ + + ]: 12 : if (slave->enabled) {
1161 : 2 : insert_bal(&bals, slave);
1162 : : }
1163 : : }
1164 : 6 : log_bals(bond, &bals);
1165 : :
1166 : : /* Shift load from the most-loaded slaves to the least-loaded slaves. */
1167 [ + + ]: 6 : while (!ovs_list_is_short(&bals)) {
1168 : 1 : struct bond_slave *from = bond_slave_from_bal_node(ovs_list_front(&bals));
1169 : 1 : struct bond_slave *to = bond_slave_from_bal_node(ovs_list_back(&bals));
1170 : : uint64_t overload;
1171 : :
1172 : 1 : overload = from->tx_bytes - to->tx_bytes;
1173 [ + - ][ - + ]: 1 : if (overload < to->tx_bytes >> 5 || overload < 100000) {
1174 : : /* The extra load on 'from' (and all less-loaded slaves), compared
1175 : : * to that of 'to' (the least-loaded slave), is less than ~3%, or
1176 : : * it is less than ~1Mbps. No point in rebalancing. */
1177 : : break;
1178 : : }
1179 : :
1180 : : /* 'from' is carrying significantly more load than 'to'. Pick a hash
1181 : : * to move from 'from' to 'to'. */
1182 : 0 : e = choose_entry_to_migrate(from, to->tx_bytes);
1183 [ # # ]: 0 : if (e) {
1184 : 0 : bond_shift_load(e, to);
1185 : :
1186 : : /* Delete element from from->entries.
1187 : : *
1188 : : * We don't add the element to to->hashes. That would only allow
1189 : : * 'e' to be migrated to another slave in this rebalancing run, and
1190 : : * there is no point in doing that. */
1191 : 0 : ovs_list_remove(&e->list_node);
1192 : :
1193 : : /* Re-sort 'bals'. */
1194 : 0 : reinsert_bal(&bals, from);
1195 : 0 : reinsert_bal(&bals, to);
1196 : 0 : rebalanced = true;
1197 : : } else {
1198 : : /* Can't usefully migrate anything away from 'from'.
1199 : : * Don't reconsider it. */
1200 : 0 : ovs_list_remove(&from->bal_node);
1201 : : }
1202 : : }
1203 : :
1204 : : /* Implement exponentially weighted moving average. A weight of 1/2 causes
1205 : : * historical data to decay to <1% in 7 rebalancing runs. 1,000,000 bytes
1206 : : * take 20 rebalancing runs to decay to 0 and get deleted entirely. */
1207 [ + + ]: 1542 : for (e = &bond->hash[0]; e <= &bond->hash[BOND_MASK]; e++) {
1208 : 1536 : e->tx_bytes /= 2;
1209 : : }
1210 : :
1211 [ + - ][ - + ]: 6 : if (use_recirc && rebalanced) {
1212 : 0 : bond_update_post_recirc_rules__(bond,true);
1213 : : }
1214 : :
1215 : : done:
1216 : 1063 : ovs_rwlock_unlock(&rwlock);
1217 : 1063 : }
1218 : :
1219 : : /* Bonding unixctl user interface functions. */
1220 : :
1221 : : static struct bond *
1222 : 109 : bond_find(const char *name) OVS_REQ_RDLOCK(rwlock)
1223 : : {
1224 : : struct bond *bond;
1225 : :
1226 [ + - ][ # # ]: 109 : HMAP_FOR_EACH_WITH_HASH (bond, hmap_node, hash_string(name, 0),
1227 : : all_bonds) {
1228 [ + - ]: 109 : if (!strcmp(bond->name, name)) {
1229 : 109 : return bond;
1230 : : }
1231 : : }
1232 : 0 : return NULL;
1233 : : }
1234 : :
1235 : : static struct bond_slave *
1236 : 0 : bond_lookup_slave(struct bond *bond, const char *slave_name)
1237 : : {
1238 : : struct bond_slave *slave;
1239 : :
1240 [ # # ][ # # ]: 0 : HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
1241 [ # # ]: 0 : if (!strcmp(slave->name, slave_name)) {
1242 : 0 : return slave;
1243 : : }
1244 : : }
1245 : 0 : return NULL;
1246 : : }
1247 : :
1248 : : static void
1249 : 0 : bond_unixctl_list(struct unixctl_conn *conn,
1250 : : int argc OVS_UNUSED, const char *argv[] OVS_UNUSED,
1251 : : void *aux OVS_UNUSED)
1252 : : {
1253 : 0 : struct ds ds = DS_EMPTY_INITIALIZER;
1254 : : const struct bond *bond;
1255 : :
1256 : 0 : ds_put_cstr(&ds, "bond\ttype\trecircID\tslaves\n");
1257 : :
1258 : 0 : ovs_rwlock_rdlock(&rwlock);
1259 [ # # ][ # # ]: 0 : HMAP_FOR_EACH (bond, hmap_node, all_bonds) {
1260 : : const struct bond_slave *slave;
1261 : : size_t i;
1262 : :
1263 : 0 : ds_put_format(&ds, "%s\t%s\t%d\t", bond->name,
1264 : : bond_mode_to_string(bond->balance), bond->recirc_id);
1265 : :
1266 : 0 : i = 0;
1267 [ # # ][ # # ]: 0 : HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
1268 [ # # ]: 0 : if (i++ > 0) {
1269 : 0 : ds_put_cstr(&ds, ", ");
1270 : : }
1271 : 0 : ds_put_cstr(&ds, slave->name);
1272 : : }
1273 : 0 : ds_put_char(&ds, '\n');
1274 : : }
1275 : 0 : ovs_rwlock_unlock(&rwlock);
1276 : 0 : unixctl_command_reply(conn, ds_cstr(&ds));
1277 : 0 : ds_destroy(&ds);
1278 : 0 : }
1279 : :
1280 : : static void
1281 : 18 : bond_print_details(struct ds *ds, const struct bond *bond)
1282 : : OVS_REQ_RDLOCK(rwlock)
1283 : : {
1284 : 18 : struct shash slave_shash = SHASH_INITIALIZER(&slave_shash);
1285 : 18 : const struct shash_node **sorted_slaves = NULL;
1286 : : const struct bond_slave *slave;
1287 : : bool may_recirc;
1288 : : uint32_t recirc_id;
1289 : : int i;
1290 : :
1291 : 18 : ds_put_format(ds, "---- %s ----\n", bond->name);
1292 : 18 : ds_put_format(ds, "bond_mode: %s\n",
1293 : : bond_mode_to_string(bond->balance));
1294 : :
1295 : 18 : may_recirc = bond_may_recirc(bond, &recirc_id, NULL);
1296 [ + + ][ + + ]: 18 : ds_put_format(ds, "bond may use recirculation: %s, Recirc-ID : %d\n",
1297 : : may_recirc ? "yes" : "no", may_recirc ? recirc_id: -1);
1298 : :
1299 : 18 : ds_put_format(ds, "bond-hash-basis: %"PRIu32"\n", bond->basis);
1300 : :
1301 : 18 : ds_put_format(ds, "updelay: %d ms\n", bond->updelay);
1302 : 18 : ds_put_format(ds, "downdelay: %d ms\n", bond->downdelay);
1303 : :
1304 [ - + ]: 18 : if (bond_is_balanced(bond)) {
1305 : 0 : ds_put_format(ds, "next rebalance: %lld ms\n",
1306 : 0 : bond->next_rebalance - time_msec());
1307 : : }
1308 : :
1309 : 18 : ds_put_cstr(ds, "lacp_status: ");
1310 [ + - + - ]: 18 : switch (bond->lacp_status) {
1311 : : case LACP_NEGOTIATED:
1312 : 13 : ds_put_cstr(ds, "negotiated\n");
1313 : 13 : break;
1314 : : case LACP_CONFIGURED:
1315 : 0 : ds_put_cstr(ds, "configured\n");
1316 : 0 : break;
1317 : : case LACP_DISABLED:
1318 : 5 : ds_put_cstr(ds, "off\n");
1319 : 5 : break;
1320 : : default:
1321 : 0 : ds_put_cstr(ds, "<unknown>\n");
1322 : 0 : break;
1323 : : }
1324 : :
1325 : 18 : ds_put_cstr(ds, "active slave mac: ");
1326 : 18 : ds_put_format(ds, ETH_ADDR_FMT, ETH_ADDR_ARGS(bond->active_slave_mac));
1327 : 18 : slave = bond_find_slave_by_mac(bond, bond->active_slave_mac);
1328 [ + + ]: 18 : ds_put_format(ds,"(%s)\n", slave ? slave->name : "none");
1329 : :
1330 [ + + ][ - + ]: 58 : HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
1331 : 40 : shash_add(&slave_shash, slave->name, slave);
1332 : : }
1333 : 18 : sorted_slaves = shash_sort(&slave_shash);
1334 : :
1335 [ + + ]: 58 : for (i = 0; i < shash_count(&slave_shash); i++) {
1336 : : struct bond_entry *be;
1337 : :
1338 : 40 : slave = sorted_slaves[i]->data;
1339 : :
1340 : : /* Basic info. */
1341 [ + + ]: 40 : ds_put_format(ds, "\nslave %s: %s\n",
1342 : 40 : slave->name, slave->enabled ? "enabled" : "disabled");
1343 [ + + ]: 40 : if (slave == bond->active_slave) {
1344 : 17 : ds_put_cstr(ds, "\tactive slave\n");
1345 : : }
1346 [ - + ]: 40 : if (slave->delay_expires != LLONG_MAX) {
1347 [ # # ]: 0 : ds_put_format(ds, "\t%s expires in %lld ms\n",
1348 : 0 : slave->enabled ? "downdelay" : "updelay",
1349 : 0 : slave->delay_expires - time_msec());
1350 : : }
1351 : :
1352 [ + + ]: 40 : ds_put_format(ds, "\tmay_enable: %s\n",
1353 : 40 : slave->may_enable ? "true" : "false");
1354 : :
1355 [ + - ]: 40 : if (!bond_is_balanced(bond)) {
1356 : 40 : continue;
1357 : : }
1358 : :
1359 : : /* Hashes. */
1360 [ # # ]: 0 : for (be = bond->hash; be <= &bond->hash[BOND_MASK]; be++) {
1361 : 0 : int hash = be - bond->hash;
1362 : : uint64_t be_tx_k;
1363 : :
1364 [ # # ]: 0 : if (be->slave != slave) {
1365 : 0 : continue;
1366 : : }
1367 : :
1368 : 0 : be_tx_k = be->tx_bytes / 1024;
1369 [ # # ]: 0 : if (be_tx_k) {
1370 : 0 : ds_put_format(ds, "\thash %d: %"PRIu64" kB load\n",
1371 : : hash, be_tx_k);
1372 : : }
1373 : :
1374 : : /* XXX How can we list the MACs assigned to hashes of SLB bonds? */
1375 : : }
1376 : : }
1377 : 18 : shash_destroy(&slave_shash);
1378 : 18 : free(sorted_slaves);
1379 : 18 : ds_put_cstr(ds, "\n");
1380 : 18 : }
1381 : :
1382 : : static void
1383 : 14 : bond_unixctl_show(struct unixctl_conn *conn,
1384 : : int argc, const char *argv[],
1385 : : void *aux OVS_UNUSED)
1386 : : {
1387 : 14 : struct ds ds = DS_EMPTY_INITIALIZER;
1388 : :
1389 : 14 : ovs_rwlock_rdlock(&rwlock);
1390 [ + + ]: 14 : if (argc > 1) {
1391 : 7 : const struct bond *bond = bond_find(argv[1]);
1392 : :
1393 [ - + ]: 7 : if (!bond) {
1394 : 0 : unixctl_command_reply_error(conn, "no such bond");
1395 : 0 : goto out;
1396 : : }
1397 : 7 : bond_print_details(&ds, bond);
1398 : : } else {
1399 : : const struct bond *bond;
1400 : :
1401 [ + + ][ - + ]: 18 : HMAP_FOR_EACH (bond, hmap_node, all_bonds) {
1402 : 11 : bond_print_details(&ds, bond);
1403 : : }
1404 : : }
1405 : :
1406 : 14 : unixctl_command_reply(conn, ds_cstr(&ds));
1407 : 14 : ds_destroy(&ds);
1408 : :
1409 : : out:
1410 : 14 : ovs_rwlock_unlock(&rwlock);
1411 : 14 : }
1412 : :
1413 : : static void
1414 : 0 : bond_unixctl_migrate(struct unixctl_conn *conn,
1415 : : int argc OVS_UNUSED, const char *argv[],
1416 : : void *aux OVS_UNUSED)
1417 : : {
1418 : 0 : const char *bond_s = argv[1];
1419 : 0 : const char *hash_s = argv[2];
1420 : 0 : const char *slave_s = argv[3];
1421 : : struct bond *bond;
1422 : : struct bond_slave *slave;
1423 : : struct bond_entry *entry;
1424 : : int hash;
1425 : :
1426 : 0 : ovs_rwlock_wrlock(&rwlock);
1427 : 0 : bond = bond_find(bond_s);
1428 [ # # ]: 0 : if (!bond) {
1429 : 0 : unixctl_command_reply_error(conn, "no such bond");
1430 : 0 : goto out;
1431 : : }
1432 : :
1433 [ # # ]: 0 : if (bond->balance != BM_SLB) {
1434 : 0 : unixctl_command_reply_error(conn, "not an SLB bond");
1435 : 0 : goto out;
1436 : : }
1437 : :
1438 [ # # ]: 0 : if (strspn(hash_s, "0123456789") == strlen(hash_s)) {
1439 : 0 : hash = atoi(hash_s) & BOND_MASK;
1440 : : } else {
1441 : 0 : unixctl_command_reply_error(conn, "bad hash");
1442 : 0 : goto out;
1443 : : }
1444 : :
1445 : 0 : slave = bond_lookup_slave(bond, slave_s);
1446 [ # # ]: 0 : if (!slave) {
1447 : 0 : unixctl_command_reply_error(conn, "no such slave");
1448 : 0 : goto out;
1449 : : }
1450 : :
1451 [ # # ]: 0 : if (!slave->enabled) {
1452 : 0 : unixctl_command_reply_error(conn, "cannot migrate to disabled slave");
1453 : 0 : goto out;
1454 : : }
1455 : :
1456 : 0 : entry = &bond->hash[hash];
1457 : 0 : bond->bond_revalidate = true;
1458 : 0 : entry->slave = slave;
1459 : 0 : unixctl_command_reply(conn, "migrated");
1460 : :
1461 : : out:
1462 : 0 : ovs_rwlock_unlock(&rwlock);
1463 : 0 : }
1464 : :
1465 : : static void
1466 : 0 : bond_unixctl_set_active_slave(struct unixctl_conn *conn,
1467 : : int argc OVS_UNUSED, const char *argv[],
1468 : : void *aux OVS_UNUSED)
1469 : : {
1470 : 0 : const char *bond_s = argv[1];
1471 : 0 : const char *slave_s = argv[2];
1472 : : struct bond *bond;
1473 : : struct bond_slave *slave;
1474 : :
1475 : 0 : ovs_rwlock_wrlock(&rwlock);
1476 : 0 : bond = bond_find(bond_s);
1477 [ # # ]: 0 : if (!bond) {
1478 : 0 : unixctl_command_reply_error(conn, "no such bond");
1479 : 0 : goto out;
1480 : : }
1481 : :
1482 : 0 : slave = bond_lookup_slave(bond, slave_s);
1483 [ # # ]: 0 : if (!slave) {
1484 : 0 : unixctl_command_reply_error(conn, "no such slave");
1485 : 0 : goto out;
1486 : : }
1487 : :
1488 [ # # ]: 0 : if (!slave->enabled) {
1489 : 0 : unixctl_command_reply_error(conn, "cannot make disabled slave active");
1490 : 0 : goto out;
1491 : : }
1492 : :
1493 [ # # ]: 0 : if (bond->active_slave != slave) {
1494 : 0 : bond->bond_revalidate = true;
1495 : 0 : bond->active_slave = slave;
1496 [ # # ]: 0 : VLOG_INFO("bond %s: active interface is now %s",
1497 : : bond->name, slave->name);
1498 : 0 : bond->send_learning_packets = true;
1499 : 0 : unixctl_command_reply(conn, "done");
1500 : 0 : bond_active_slave_changed(bond);
1501 : : } else {
1502 : 0 : unixctl_command_reply(conn, "no change");
1503 : : }
1504 : : out:
1505 : 0 : ovs_rwlock_unlock(&rwlock);
1506 : 0 : }
1507 : :
1508 : : static void
1509 : 0 : enable_slave(struct unixctl_conn *conn, const char *argv[], bool enable)
1510 : : {
1511 : 0 : const char *bond_s = argv[1];
1512 : 0 : const char *slave_s = argv[2];
1513 : : struct bond *bond;
1514 : : struct bond_slave *slave;
1515 : :
1516 : 0 : ovs_rwlock_wrlock(&rwlock);
1517 : 0 : bond = bond_find(bond_s);
1518 [ # # ]: 0 : if (!bond) {
1519 : 0 : unixctl_command_reply_error(conn, "no such bond");
1520 : 0 : goto out;
1521 : : }
1522 : :
1523 : 0 : slave = bond_lookup_slave(bond, slave_s);
1524 [ # # ]: 0 : if (!slave) {
1525 : 0 : unixctl_command_reply_error(conn, "no such slave");
1526 : 0 : goto out;
1527 : : }
1528 : :
1529 : 0 : bond_enable_slave(slave, enable);
1530 [ # # ]: 0 : unixctl_command_reply(conn, enable ? "enabled" : "disabled");
1531 : :
1532 : : out:
1533 : 0 : ovs_rwlock_unlock(&rwlock);
1534 : 0 : }
1535 : :
1536 : : static void
1537 : 0 : bond_unixctl_enable_slave(struct unixctl_conn *conn,
1538 : : int argc OVS_UNUSED, const char *argv[],
1539 : : void *aux OVS_UNUSED)
1540 : : {
1541 : 0 : enable_slave(conn, argv, true);
1542 : 0 : }
1543 : :
1544 : : static void
1545 : 0 : bond_unixctl_disable_slave(struct unixctl_conn *conn,
1546 : : int argc OVS_UNUSED, const char *argv[],
1547 : : void *aux OVS_UNUSED)
1548 : : {
1549 : 0 : enable_slave(conn, argv, false);
1550 : 0 : }
1551 : :
1552 : : static void
1553 : 12 : bond_unixctl_hash(struct unixctl_conn *conn, int argc, const char *argv[],
1554 : : void *aux OVS_UNUSED)
1555 : : {
1556 : 12 : const char *mac_s = argv[1];
1557 [ + + ]: 12 : const char *vlan_s = argc > 2 ? argv[2] : NULL;
1558 [ + + ]: 12 : const char *basis_s = argc > 3 ? argv[3] : NULL;
1559 : : struct eth_addr mac;
1560 : : uint8_t hash;
1561 : : char *hash_cstr;
1562 : : unsigned int vlan;
1563 : : uint32_t basis;
1564 : :
1565 [ + + ]: 12 : if (vlan_s) {
1566 [ + - ]: 8 : if (!ovs_scan(vlan_s, "%u", &vlan)) {
1567 : 8 : unixctl_command_reply_error(conn, "invalid vlan");
1568 : 8 : return;
1569 : : }
1570 : : } else {
1571 : 4 : vlan = 0;
1572 : : }
1573 : :
1574 [ - + ]: 4 : if (basis_s) {
1575 [ # # ]: 0 : if (!ovs_scan(basis_s, "%"SCNu32, &basis)) {
1576 : 0 : unixctl_command_reply_error(conn, "invalid basis");
1577 : 0 : return;
1578 : : }
1579 : : } else {
1580 : 4 : basis = 0;
1581 : : }
1582 : :
1583 [ - + ]: 4 : if (ovs_scan(mac_s, ETH_ADDR_SCAN_FMT, ETH_ADDR_SCAN_ARGS(mac))) {
1584 : 0 : hash = bond_hash_src(mac, vlan, basis) & BOND_MASK;
1585 : :
1586 : 0 : hash_cstr = xasprintf("%u", hash);
1587 : 0 : unixctl_command_reply(conn, hash_cstr);
1588 : 0 : free(hash_cstr);
1589 : : } else {
1590 : 4 : unixctl_command_reply_error(conn, "invalid mac");
1591 : : }
1592 : : }
1593 : :
1594 : : void
1595 : 617 : bond_init(void)
1596 : : {
1597 : 617 : unixctl_command_register("bond/list", "", 0, 0, bond_unixctl_list, NULL);
1598 : 617 : unixctl_command_register("bond/show", "[port]", 0, 1, bond_unixctl_show,
1599 : : NULL);
1600 : 617 : unixctl_command_register("bond/migrate", "port hash slave", 3, 3,
1601 : : bond_unixctl_migrate, NULL);
1602 : 617 : unixctl_command_register("bond/set-active-slave", "port slave", 2, 2,
1603 : : bond_unixctl_set_active_slave, NULL);
1604 : 617 : unixctl_command_register("bond/enable-slave", "port slave", 2, 2,
1605 : : bond_unixctl_enable_slave, NULL);
1606 : 617 : unixctl_command_register("bond/disable-slave", "port slave", 2, 2,
1607 : : bond_unixctl_disable_slave, NULL);
1608 : 617 : unixctl_command_register("bond/hash", "mac [vlan] [basis]", 1, 3,
1609 : : bond_unixctl_hash, NULL);
1610 : 617 : }
1611 : :
1612 : : static void
1613 : 26 : bond_entry_reset(struct bond *bond)
1614 : : {
1615 [ + + ]: 26 : if (bond->balance != BM_AB) {
1616 : 12 : size_t hash_len = BOND_BUCKETS * sizeof *bond->hash;
1617 : :
1618 [ + - ]: 12 : if (!bond->hash) {
1619 : 12 : bond->hash = xmalloc(hash_len);
1620 : : }
1621 : 12 : memset(bond->hash, 0, hash_len);
1622 : :
1623 : 12 : bond->next_rebalance = time_msec() + bond->rebalance_interval;
1624 : : } else {
1625 : 14 : free(bond->hash);
1626 : 14 : bond->hash = NULL;
1627 : : }
1628 : 26 : }
1629 : :
1630 : : static struct bond_slave *
1631 : 27235 : bond_slave_lookup(struct bond *bond, const void *slave_)
1632 : : {
1633 : : struct bond_slave *slave;
1634 : :
1635 [ + + ][ - + ]: 45813 : HMAP_FOR_EACH_IN_BUCKET (slave, hmap_node, hash_pointer(slave_, 0),
1636 : : &bond->slaves) {
1637 [ + + ]: 45772 : if (slave->aux == slave_) {
1638 : 27194 : return slave;
1639 : : }
1640 : : }
1641 : :
1642 : 41 : return NULL;
1643 : : }
1644 : :
1645 : : static void
1646 : 138 : bond_enable_slave(struct bond_slave *slave, bool enable)
1647 : : {
1648 : 138 : slave->delay_expires = LLONG_MAX;
1649 [ + + ]: 138 : if (enable != slave->enabled) {
1650 : 104 : slave->bond->bond_revalidate = true;
1651 : 104 : slave->enabled = enable;
1652 : :
1653 : 104 : ovs_mutex_lock(&slave->bond->mutex);
1654 [ + + ]: 104 : if (enable) {
1655 : 52 : ovs_list_insert(&slave->bond->enabled_slaves, &slave->list_node);
1656 : : } else {
1657 : 52 : ovs_list_remove(&slave->list_node);
1658 : : }
1659 : 104 : ovs_mutex_unlock(&slave->bond->mutex);
1660 : :
1661 [ + - ][ + + ]: 104 : VLOG_INFO("interface %s: %s", slave->name,
1662 : : slave->enabled ? "enabled" : "disabled");
1663 : : }
1664 : 138 : }
1665 : :
1666 : : static void
1667 : 14728 : bond_link_status_update(struct bond_slave *slave)
1668 : : {
1669 : 14728 : struct bond *bond = slave->bond;
1670 : : bool up;
1671 : :
1672 [ + + ][ + + ]: 14728 : up = netdev_get_carrier(slave->netdev) && slave->may_enable;
1673 [ + + ]: 14728 : if ((up == slave->enabled) != (slave->delay_expires == LLONG_MAX)) {
1674 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
1675 [ + - ][ + + ]: 56 : VLOG_INFO_RL(&rl, "interface %s: link state %s",
1676 : : slave->name, up ? "up" : "down");
1677 [ - + ]: 56 : if (up == slave->enabled) {
1678 : 0 : slave->delay_expires = LLONG_MAX;
1679 [ # # ][ # # ]: 0 : VLOG_INFO_RL(&rl, "interface %s: will not be %s",
1680 : : slave->name, up ? "disabled" : "enabled");
1681 : : } else {
1682 : 112 : int delay = (bond->lacp_status != LACP_DISABLED ? 0
1683 [ + + ][ + + ]: 56 : : up ? bond->updelay : bond->downdelay);
1684 : 56 : slave->delay_expires = time_msec() + delay;
1685 [ - + ]: 56 : if (delay) {
1686 [ # # ][ # # ]: 0 : VLOG_INFO_RL(&rl, "interface %s: will be %s if it stays %s "
[ # # ]
1687 : : "for %d ms",
1688 : : slave->name,
1689 : : up ? "enabled" : "disabled",
1690 : : up ? "up" : "down",
1691 : : delay);
1692 : : }
1693 : : }
1694 : : }
1695 : :
1696 [ + + ]: 14728 : if (time_msec() >= slave->delay_expires) {
1697 : 56 : bond_enable_slave(slave, up);
1698 : : }
1699 : 14728 : }
1700 : :
1701 : : static unsigned int
1702 : 6190 : bond_hash_src(const struct eth_addr mac, uint16_t vlan, uint32_t basis)
1703 : : {
1704 : 6190 : return hash_mac(mac, vlan, basis);
1705 : : }
1706 : :
1707 : : static unsigned int
1708 : 309 : bond_hash_tcp(const struct flow *flow, uint16_t vlan, uint32_t basis)
1709 : : {
1710 : 309 : struct flow hash_flow = *flow;
1711 : 309 : hash_flow.vlan_tci = htons(vlan);
1712 : :
1713 : : /* The symmetric quality of this hash function is not required, but
1714 : : * flow_hash_symmetric_l4 already exists, and is sufficient for our
1715 : : * purposes, so we use it out of convenience. */
1716 : 309 : return flow_hash_symmetric_l4(&hash_flow, basis);
1717 : : }
1718 : :
1719 : : static unsigned int
1720 : 6499 : bond_hash(const struct bond *bond, const struct flow *flow, uint16_t vlan)
1721 : : {
1722 [ + + ][ - + ]: 6499 : ovs_assert(bond->balance == BM_TCP || bond->balance == BM_SLB);
1723 : :
1724 : 6499 : return (bond->balance == BM_TCP
1725 : 309 : ? bond_hash_tcp(flow, vlan, bond->basis)
1726 [ + + ]: 6499 : : bond_hash_src(flow->dl_src, vlan, bond->basis));
1727 : : }
1728 : :
1729 : : static struct bond_entry *
1730 : 6499 : lookup_bond_entry(const struct bond *bond, const struct flow *flow,
1731 : : uint16_t vlan)
1732 : : {
1733 : 6499 : return &bond->hash[bond_hash(bond, flow, vlan) & BOND_MASK];
1734 : : }
1735 : :
1736 : : /* Selects and returns an enabled slave from the 'enabled_slaves' list
1737 : : * in a round-robin fashion. If the 'enabled_slaves' list is empty,
1738 : : * returns NULL. */
1739 : : static struct bond_slave *
1740 : 110 : get_enabled_slave(struct bond *bond)
1741 : : {
1742 : : struct ovs_list *node;
1743 : :
1744 : 110 : ovs_mutex_lock(&bond->mutex);
1745 [ + + ]: 110 : if (ovs_list_is_empty(&bond->enabled_slaves)) {
1746 : 25 : ovs_mutex_unlock(&bond->mutex);
1747 : 25 : return NULL;
1748 : : }
1749 : :
1750 : 85 : node = ovs_list_pop_front(&bond->enabled_slaves);
1751 : 85 : ovs_list_push_back(&bond->enabled_slaves, node);
1752 : 85 : ovs_mutex_unlock(&bond->mutex);
1753 : :
1754 : 85 : return CONTAINER_OF(node, struct bond_slave, list_node);
1755 : : }
1756 : :
1757 : : static struct bond_slave *
1758 : 6422 : choose_output_slave(const struct bond *bond, const struct flow *flow,
1759 : : struct flow_wildcards *wc, uint16_t vlan)
1760 : : {
1761 : : struct bond_entry *e;
1762 : : int balance;
1763 : :
1764 : 6422 : balance = bond->balance;
1765 [ - + ]: 6422 : if (bond->lacp_status == LACP_CONFIGURED) {
1766 : : /* LACP has been configured on this bond but negotiations were
1767 : : * unsuccussful. If lacp_fallback_ab is enabled use active-
1768 : : * backup mode else drop all traffic. */
1769 [ # # ]: 0 : if (!bond->lacp_fallback_ab) {
1770 : 0 : return NULL;
1771 : : }
1772 : 0 : balance = BM_AB;
1773 : : }
1774 : :
1775 [ + + + - ]: 6422 : switch (balance) {
1776 : : case BM_AB:
1777 : 26 : return bond->active_slave;
1778 : :
1779 : : case BM_TCP:
1780 [ - + ]: 309 : if (bond->lacp_status != LACP_NEGOTIATED) {
1781 : : /* Must have LACP negotiations for TCP balanced bonds. */
1782 : 0 : return NULL;
1783 : : }
1784 [ - + ]: 309 : if (wc) {
1785 : 0 : flow_mask_hash_fields(flow, wc, NX_HASH_FIELDS_SYMMETRIC_L4);
1786 : : }
1787 : : /* Fall Through. */
1788 : : case BM_SLB:
1789 [ + + ]: 6396 : if (wc) {
1790 : 6087 : flow_mask_hash_fields(flow, wc, NX_HASH_FIELDS_ETH_SRC);
1791 : : }
1792 : 6396 : e = lookup_bond_entry(bond, flow, vlan);
1793 [ + + ][ + + ]: 6396 : if (!e->slave || !e->slave->enabled) {
1794 : 110 : e->slave = get_enabled_slave(CONST_CAST(struct bond*, bond));
1795 : : }
1796 : 6396 : return e->slave;
1797 : :
1798 : : default:
1799 : 0 : OVS_NOT_REACHED();
1800 : : }
1801 : : }
1802 : :
1803 : : static struct bond_slave *
1804 : 400 : bond_choose_slave(const struct bond *bond)
1805 : : {
1806 : : struct bond_slave *slave, *best;
1807 : :
1808 : : /* Find the last active slave. */
1809 : 400 : slave = bond_find_slave_by_mac(bond, bond->active_slave_mac);
1810 [ + + ][ - + ]: 400 : if (slave && slave->enabled) {
1811 : 0 : return slave;
1812 : : }
1813 : :
1814 : : /* Find an enabled slave. */
1815 [ + + ][ - + ]: 1157 : HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
1816 [ + + ]: 784 : if (slave->enabled) {
1817 : 27 : return slave;
1818 : : }
1819 : : }
1820 : :
1821 : : /* All interfaces are disabled. Find an interface that will be enabled
1822 : : * after its updelay expires. */
1823 : 373 : best = NULL;
1824 [ + + ][ - + ]: 1128 : HMAP_FOR_EACH (slave, hmap_node, &bond->slaves) {
1825 [ - + ]: 755 : if (slave->delay_expires != LLONG_MAX
1826 [ # # ]: 0 : && slave->may_enable
1827 [ # # ][ # # ]: 0 : && (!best || slave->delay_expires < best->delay_expires)) {
1828 : 0 : best = slave;
1829 : : }
1830 : : }
1831 : 373 : return best;
1832 : : }
1833 : :
1834 : : static void
1835 : 400 : bond_choose_active_slave(struct bond *bond)
1836 : : {
1837 : : static struct vlog_rate_limit rl = VLOG_RATE_LIMIT_INIT(5, 20);
1838 : 400 : struct bond_slave *old_active_slave = bond->active_slave;
1839 : :
1840 : 400 : bond->active_slave = bond_choose_slave(bond);
1841 [ + + ]: 400 : if (bond->active_slave) {
1842 [ + - ]: 27 : if (bond->active_slave->enabled) {
1843 [ + - ]: 27 : VLOG_INFO_RL(&rl, "bond %s: active interface is now %s",
1844 : : bond->name, bond->active_slave->name);
1845 : : } else {
1846 [ # # ]: 0 : VLOG_INFO_RL(&rl, "bond %s: active interface is now %s, skipping "
1847 : : "remaining %lld ms updelay (since no interface was "
1848 : : "enabled)", bond->name, bond->active_slave->name,
1849 : : bond->active_slave->delay_expires - time_msec());
1850 : 0 : bond_enable_slave(bond->active_slave, true);
1851 : : }
1852 : :
1853 : 27 : bond->send_learning_packets = true;
1854 : :
1855 [ + - ]: 27 : if (bond->active_slave != old_active_slave) {
1856 : 27 : bond_active_slave_changed(bond);
1857 : : }
1858 [ + + ]: 373 : } else if (old_active_slave) {
1859 [ + - ]: 17 : VLOG_INFO_RL(&rl, "bond %s: all interfaces disabled", bond->name);
1860 : : }
1861 : 400 : }
1862 : :
1863 : : /*
1864 : : * Return true if bond has unstored active slave change.
1865 : : * If return true, 'mac' will store the bond's current active slave's
1866 : : * MAC address. */
1867 : : bool
1868 : 102 : bond_get_changed_active_slave(const char *name, struct eth_addr *mac,
1869 : : bool force)
1870 : : {
1871 : : struct bond *bond;
1872 : :
1873 : 102 : ovs_rwlock_wrlock(&rwlock);
1874 : 102 : bond = bond_find(name);
1875 [ + - ]: 102 : if (bond) {
1876 [ + + ][ + + ]: 102 : if (bond->active_slave_changed || force) {
1877 : 46 : *mac = bond->active_slave_mac;
1878 : 46 : bond->active_slave_changed = false;
1879 : 46 : ovs_rwlock_unlock(&rwlock);
1880 : 46 : return true;
1881 : : }
1882 : : }
1883 : 56 : ovs_rwlock_unlock(&rwlock);
1884 : :
1885 : 56 : return false;
1886 : : }
|