device: add support for configuring bonding devices
[project/netifd.git] / bonding.c
1 /*
2 * netifd - network interface daemon
3 * Copyright (C) 2021 Felix Fietkau <nbd@nbd.name>
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License version 2
7 * as published by the Free Software Foundation
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 */
14 #include <string.h>
15 #include <stdlib.h>
16 #include <stdio.h>
17
18 #include "netifd.h"
19 #include "device.h"
20 #include "system.h"
21
22 struct bonding_device {
23 struct device dev;
24 device_state_cb set_state;
25
26 struct blob_attr *port_list;
27 struct vlist_tree ports;
28 int n_present;
29 int n_failed;
30
31 struct bonding_port *primary_port;
32 struct uloop_timeout retry;
33
34 struct bonding_config config;
35 struct blob_attr *config_data;
36 bool has_macaddr;
37 bool force_active;
38 bool active;
39 };
40
41 struct bonding_port {
42 struct vlist_node node;
43 struct bonding_device *bdev;
44 struct device_user dev;
45 bool set_primary;
46 bool present;
47 bool active;
48 char name[];
49 };
50
51 enum {
52 BOND_ATTR_PORTS,
53
54 BOND_ATTR_POLICY,
55 BOND_ATTR_XMIT_HASH_POLICY,
56 BOND_ATTR_ALL_PORTS_ACTIVE,
57
58 BOND_ATTR_MIN_LINKS,
59 BOND_ATTR_AD_ACTOR_SYSTEM,
60 BOND_ATTR_AD_ACTOR_SYS_PRIO,
61 BOND_ATTR_AD_SELECT,
62 BOND_ATTR_LACP_RATE,
63
64 BOND_ATTR_PACKETS_PER_PORT,
65 BOND_ATTR_LP_INTERVAL,
66 BOND_ATTR_DYNAMIC_LB,
67 BOND_ATTR_RESEND_IGMP,
68
69 BOND_ATTR_NUM_PEER_NOTIF,
70 BOND_ATTR_PRIMARY,
71 BOND_ATTR_PRIMARY_RESELECT,
72 BOND_ATTR_FAILOVER_MAC,
73
74 BOND_ATTR_MON_MODE,
75 BOND_ATTR_MON_INTERVAL,
76 BOND_ATTR_ARP_TARGET,
77 BOND_ATTR_ARP_ALL_TARGETS,
78 BOND_ATTR_ARP_VALIDATE,
79 BOND_ATTR_USE_CARRIER,
80 BOND_ATTR_UPDELAY,
81 BOND_ATTR_DOWNDELAY,
82
83 __BOND_ATTR_MAX,
84 };
85
86 static const struct blobmsg_policy bonding_attrs[__BOND_ATTR_MAX] = {
87 [BOND_ATTR_PORTS] = { "ports", BLOBMSG_TYPE_ARRAY },
88 [BOND_ATTR_POLICY] = { "policy", BLOBMSG_TYPE_STRING },
89 [BOND_ATTR_XMIT_HASH_POLICY] = { "xmit_hash_policy", BLOBMSG_TYPE_STRING },
90 [BOND_ATTR_ALL_PORTS_ACTIVE] = { "all_ports_active", BLOBMSG_TYPE_BOOL },
91 [BOND_ATTR_MIN_LINKS] = { "min_links", BLOBMSG_TYPE_INT32 },
92 [BOND_ATTR_AD_ACTOR_SYSTEM] = { "ad_actor_system", BLOBMSG_TYPE_STRING },
93 [BOND_ATTR_AD_ACTOR_SYS_PRIO] = { "ad_actor_sys_prio", BLOBMSG_TYPE_INT32 },
94 [BOND_ATTR_AD_SELECT] = { "ad_select", BLOBMSG_TYPE_STRING },
95 [BOND_ATTR_LACP_RATE] = { "lacp_rate", BLOBMSG_TYPE_STRING },
96 [BOND_ATTR_PACKETS_PER_PORT] = { "packets_per_port", BLOBMSG_TYPE_INT32 },
97 [BOND_ATTR_LP_INTERVAL] = { "lp_interval", BLOBMSG_TYPE_INT32 },
98 [BOND_ATTR_DYNAMIC_LB] = { "dynamic_lb", BLOBMSG_TYPE_BOOL },
99 [BOND_ATTR_RESEND_IGMP] = { "resend_igmp", BLOBMSG_TYPE_INT32 },
100 [BOND_ATTR_NUM_PEER_NOTIF] = { "num_peer_notif", BLOBMSG_TYPE_INT32 },
101 [BOND_ATTR_PRIMARY] = { "primary", BLOBMSG_TYPE_STRING },
102 [BOND_ATTR_PRIMARY_RESELECT] = { "primary_reselect", BLOBMSG_TYPE_STRING },
103 [BOND_ATTR_FAILOVER_MAC] = { "failover_mac", BLOBMSG_TYPE_STRING },
104 [BOND_ATTR_MON_MODE] = { "monitor_mode", BLOBMSG_TYPE_STRING },
105 [BOND_ATTR_MON_INTERVAL] = { "monitor_interval", BLOBMSG_TYPE_INT32 },
106 [BOND_ATTR_ARP_TARGET] = { "arp_target", BLOBMSG_TYPE_ARRAY },
107 [BOND_ATTR_ARP_ALL_TARGETS] = { "arp_all_targets", BLOBMSG_TYPE_BOOL },
108 [BOND_ATTR_ARP_VALIDATE] = { "arp_validate", BLOBMSG_TYPE_STRING },
109 [BOND_ATTR_USE_CARRIER] = { "use_carrier", BLOBMSG_TYPE_BOOL },
110 [BOND_ATTR_UPDELAY] = { "updelay", BLOBMSG_TYPE_INT32 },
111 [BOND_ATTR_DOWNDELAY] = { "downdelay", BLOBMSG_TYPE_INT32 },
112 };
113
114 static const struct uci_blob_param_info bonding_attr_info[__BOND_ATTR_MAX] = {
115 [BOND_ATTR_PORTS] = { .type = BLOBMSG_TYPE_STRING },
116 [BOND_ATTR_ARP_TARGET] = { .type = BLOBMSG_TYPE_STRING },
117 };
118
119 static const struct uci_blob_param_list bonding_attr_list = {
120 .n_params = __BOND_ATTR_MAX,
121 .params = bonding_attrs,
122 .info = bonding_attr_info,
123
124 .n_next = 1,
125 .next = { &device_attr_list },
126 };
127
128 static void
129 bonding_reset_primary(struct bonding_device *bdev)
130 {
131 struct bonding_port *bp;
132
133 bdev->primary_port = NULL;
134 if (!bdev->has_macaddr)
135 bdev->dev.settings.flags &= ~DEV_OPT_MACADDR;
136
137 vlist_for_each_element(&bdev->ports, bp, node) {
138 uint8_t *macaddr;
139
140 if (!bp->present)
141 continue;
142
143 if (bdev->primary_port && !bp->set_primary)
144 continue;
145
146 bdev->primary_port = bp;
147 if (bdev->has_macaddr)
148 continue;
149
150 if (bp->dev.dev->settings.flags & DEV_OPT_MACADDR)
151 macaddr = bp->dev.dev->settings.macaddr;
152 else
153 macaddr = bp->dev.dev->orig_settings.macaddr;
154 memcpy(bdev->dev.settings.macaddr, macaddr, 6);
155 bdev->dev.settings.flags |= DEV_OPT_MACADDR;
156 }
157 }
158
159 static int
160 bonding_disable_port(struct bonding_port *bp, bool keep_dev)
161 {
162 struct bonding_device *bdev = bp->bdev;
163
164 if (!bp->present || !bp->active)
165 return 0;
166
167 bp->active = false;
168
169 system_bonding_set_port(&bdev->dev, bp->dev.dev, false, bp->set_primary);
170 if (!keep_dev)
171 device_release(&bp->dev);
172
173 return 0;
174 }
175
176 static void
177 bonding_remove_port(struct bonding_port *bp)
178 {
179 struct bonding_device *bdev = bp->bdev;
180
181 if (!bp->present)
182 return;
183
184 if (bdev->dev.active)
185 bonding_disable_port(bp, false);
186
187 bp->present = false;
188 bp->bdev->n_present--;
189
190 if (bp == bdev->primary_port)
191 bonding_reset_primary(bdev);
192
193 bdev->force_active = false;
194 if (bdev->n_present == 0)
195 device_set_present(&bdev->dev, false);
196 }
197
198 static int
199 bonding_set_active(struct bonding_device *bdev, bool active)
200 {
201 int ret;
202
203 if (bdev->active == active)
204 return 0;
205
206 ret = system_bonding_set_device(&bdev->dev, active ? &bdev->config : NULL);
207 if (ret < 0)
208 return ret;
209
210 bdev->active = active;
211 return 0;
212 }
213
214 static int
215 bonding_enable_port(struct bonding_port *bp)
216 {
217 struct bonding_device *bdev = bp->bdev;
218 struct device *dev;
219 int ret;
220
221 if (!bp->present)
222 return 0;
223
224 ret = bonding_set_active(bdev, true);
225 if (ret)
226 goto error;
227
228 /* Disable IPv6 for bonding ports */
229 if (!(bp->dev.dev->settings.flags & DEV_OPT_IPV6)) {
230 bp->dev.dev->settings.ipv6 = 0;
231 bp->dev.dev->settings.flags |= DEV_OPT_IPV6;
232 }
233
234 ret = device_claim(&bp->dev);
235 if (ret < 0)
236 goto error;
237
238 dev = bp->dev.dev;
239 if (dev->settings.auth && !dev->auth_status)
240 return -1;
241
242 if (bp->active)
243 return 0;
244
245 ret = system_bonding_set_port(&bdev->dev, bp->dev.dev, true, bp->set_primary);
246 if (ret < 0) {
247 D(DEVICE, "Bonding port %s could not be added\n", bp->dev.dev->ifname);
248 goto error;
249 }
250
251 bp->active = true;
252 device_set_present(&bdev->dev, true);
253
254 return 0;
255
256 error:
257 bdev->n_failed++;
258 bp->present = false;
259 bdev->n_present--;
260 device_release(&bp->dev);
261
262 return ret;
263 }
264
265 static void
266 bonding_port_cb(struct device_user *dep, enum device_event ev)
267 {
268 struct bonding_port *bp = container_of(dep, struct bonding_port, dev);
269 struct bonding_device *bdev = bp->bdev;
270 struct device *dev = dep->dev;
271
272 switch (ev) {
273 case DEV_EVENT_ADD:
274 if (bp->present)
275 break;
276
277 bp->present = true;
278 bdev->n_present++;
279
280 if (bdev->n_present == 1)
281 device_set_present(&bdev->dev, true);
282 fallthrough;
283 case DEV_EVENT_AUTH_UP:
284 if (!bdev->dev.active)
285 break;
286
287 if (bonding_enable_port(bp))
288 break;
289
290 /*
291 * Adding a bonding port can overwrite the bonding device mtu
292 * in the kernel, apply the bonding settings in case the
293 * bonding device mtu is set
294 */
295 system_if_apply_settings(&bdev->dev, &bdev->dev.settings,
296 DEV_OPT_MTU | DEV_OPT_MTU6);
297 break;
298 case DEV_EVENT_LINK_DOWN:
299 if (!dev->settings.auth)
300 break;
301
302 bonding_disable_port(bp, true);
303 break;
304 case DEV_EVENT_REMOVE:
305 if (dep->hotplug) {
306 vlist_delete(&bdev->ports, &bp->node);
307 return;
308 }
309
310 if (bp->present)
311 bonding_remove_port(bp);
312
313 break;
314 default:
315 return;
316 }
317 }
318
319 static struct bonding_port *
320 bonding_create_port(struct bonding_device *bdev, const char *name,
321 struct device *dev, bool hotplug)
322 {
323 struct bonding_port *bp;
324
325 bp = calloc(1, sizeof(*bp) + strlen(name) + 1);
326 if (!bp)
327 return NULL;
328
329 bp->bdev = bdev;
330 bp->dev.cb = bonding_port_cb;
331 bp->dev.hotplug = hotplug;
332 strcpy(bp->name, name);
333 bp->dev.dev = dev;
334 vlist_add(&bdev->ports, &bp->node, bp->name);
335 /*
336 * Need to look up the bonding port again as the above
337 * created pointer will be freed in case the bonding port
338 * already existed
339 */
340 if (!hotplug)
341 return bp;
342
343 bp = vlist_find(&bdev->ports, name, bp, node);
344 if (bp)
345 bp->node.version = -1;
346
347 return bp;
348 }
349
350 static void
351 bonding_config_init(struct device *dev)
352 {
353 struct bonding_device *bdev;
354 struct blob_attr *cur;
355 int rem;
356
357 bdev = container_of(dev, struct bonding_device, dev);
358
359 bdev->n_failed = 0;
360
361 vlist_update(&bdev->ports);
362 blobmsg_for_each_attr(cur, bdev->port_list, rem) {
363 const char *name = blobmsg_get_string(cur);
364
365 dev = device_get(name, true);
366 if (!dev)
367 continue;
368
369 bonding_create_port(bdev, name, dev, false);
370 }
371 vlist_flush(&bdev->ports);
372
373 if (bdev->n_failed)
374 uloop_timeout_set(&bdev->retry, 100);
375 }
376
377 static void
378 bonding_apply_settings(struct bonding_device *bdev, struct blob_attr **tb)
379 {
380 struct bonding_config *cfg = &bdev->config;
381 struct blob_attr *cur;
382
383 /* defaults */
384 memset(cfg, 0, sizeof(*cfg));
385 cfg->resend_igmp = 1;
386 cfg->ad_actor_sys_prio = 65535;
387 cfg->lp_interval = 1;
388 cfg->num_peer_notif = 1;
389
390 #define cfg_item(_type, _field, _attr) \
391 do { \
392 if ((cur = tb[BOND_ATTR_##_attr]) != NULL) \
393 cfg->_field = blobmsg_get_##_type(cur); \
394 } while (0)
395
396 if ((cur = tb[BOND_ATTR_POLICY]) != NULL) {
397 const char *policy = blobmsg_get_string(cur);
398 int i;
399
400 for (i = 0; i < ARRAY_SIZE(bonding_policy_str); i++) {
401 if (strcmp(policy, bonding_policy_str[i]) != 0)
402 continue;
403
404 cfg->policy = i;
405 break;
406 }
407 }
408
409 cfg_item(string, xmit_hash_policy, XMIT_HASH_POLICY);
410 cfg_item(bool, all_ports_active, ALL_PORTS_ACTIVE);
411 cfg_item(u32, min_links, MIN_LINKS);
412 cfg_item(string, ad_actor_system, AD_ACTOR_SYSTEM);
413 cfg_item(u32, ad_actor_sys_prio, AD_ACTOR_SYS_PRIO);
414 cfg_item(string, ad_select, AD_SELECT);
415 cfg_item(string, lacp_rate, LACP_RATE);
416 cfg_item(u32, packets_per_port, PACKETS_PER_PORT);
417 cfg_item(u32, lp_interval, LP_INTERVAL);
418 cfg_item(bool, dynamic_lb, DYNAMIC_LB);
419 cfg_item(u32, resend_igmp, RESEND_IGMP);
420 cfg_item(u32, num_peer_notif, NUM_PEER_NOTIF);
421 cfg_item(string, primary, PRIMARY);
422 cfg_item(string, primary_reselect, PRIMARY_RESELECT);
423 cfg_item(string, failover_mac, FAILOVER_MAC);
424 cfg_item(u32, monitor_interval, MON_INTERVAL);
425 cfg_item(bool, arp_all_targets, ARP_ALL_TARGETS);
426 cfg_item(string, arp_validate, ARP_VALIDATE);
427 cfg_item(bool, use_carrier, USE_CARRIER);
428 cfg_item(u32, updelay, UPDELAY);
429 cfg_item(u32, downdelay, DOWNDELAY);
430
431 if ((cur = tb[BOND_ATTR_MON_MODE]) != NULL &&
432 !strcmp(blobmsg_get_string(cur), "arp"))
433 cfg->monitor_arp = true;
434 cfg->arp_target = tb[BOND_ATTR_ARP_TARGET];
435 #undef cfg_item
436 }
437
438 static enum dev_change_type
439 bonding_reload(struct device *dev, struct blob_attr *attr)
440 {
441 struct blob_attr *tb_dev[__DEV_ATTR_MAX];
442 struct blob_attr *tb_b[__BOND_ATTR_MAX];
443 enum dev_change_type ret = DEV_CONFIG_APPLIED;
444 unsigned long diff;
445 struct bonding_device *bdev;
446
447 BUILD_BUG_ON(sizeof(diff) < __BOND_ATTR_MAX / 8);
448 BUILD_BUG_ON(sizeof(diff) < __DEV_ATTR_MAX / 8);
449
450 bdev = container_of(dev, struct bonding_device, dev);
451 attr = blob_memdup(attr);
452
453 blobmsg_parse(device_attr_list.params, __DEV_ATTR_MAX, tb_dev,
454 blob_data(attr), blob_len(attr));
455 blobmsg_parse(bonding_attrs, __BOND_ATTR_MAX, tb_b,
456 blob_data(attr), blob_len(attr));
457
458 bdev->has_macaddr = tb_dev[DEV_ATTR_MACADDR];
459 if (bdev->primary_port && !bdev->primary_port->set_primary &&
460 tb_dev[DEV_ATTR_MACADDR])
461 bdev->primary_port = NULL;
462
463 bdev->port_list = tb_b[BOND_ATTR_PORTS];
464 device_init_settings(dev, tb_dev);
465 bonding_apply_settings(bdev, tb_b);
466
467 if (bdev->config_data) {
468 struct blob_attr *otb_dev[__DEV_ATTR_MAX];
469 struct blob_attr *otb_b[__BOND_ATTR_MAX];
470
471 blobmsg_parse(device_attr_list.params, __DEV_ATTR_MAX, otb_dev,
472 blob_data(bdev->config_data), blob_len(bdev->config_data));
473
474 diff = 0;
475 uci_blob_diff(tb_dev, otb_dev, &device_attr_list, &diff);
476 if (diff)
477 ret = DEV_CONFIG_RESTART;
478
479 blobmsg_parse(bonding_attrs, __BOND_ATTR_MAX, otb_b,
480 blob_data(bdev->config_data), blob_len(bdev->config_data));
481
482 diff = 0;
483 uci_blob_diff(tb_b, otb_b, &bonding_attr_list, &diff);
484 if (diff & ~(1 << BOND_ATTR_PORTS))
485 ret = DEV_CONFIG_RESTART;
486
487 bonding_config_init(dev);
488 }
489
490 free(bdev->config_data);
491 bdev->config_data = attr;
492
493 return ret;
494 }
495
496 static int
497 bonding_hotplug_add(struct device *dev, struct device *port, struct blob_attr *vlan)
498 {
499 struct bonding_device *bdev = container_of(dev, struct bonding_device, dev);
500 struct bonding_port *bp;
501
502 bp = vlist_find(&bdev->ports, port->ifname, bp, node);
503 if (!bp)
504 bonding_create_port(bdev, port->ifname, port, true);
505
506 return 0;
507 }
508
509 static int
510 bonding_hotplug_del(struct device *dev, struct device *port, struct blob_attr *vlan)
511 {
512 struct bonding_device *bdev = container_of(dev, struct bonding_device, dev);
513 struct bonding_port *bp;
514
515 bp = vlist_find(&bdev->ports, port->ifname, bp, node);
516 if (!bp)
517 return UBUS_STATUS_NOT_FOUND;
518
519 if (bp->dev.hotplug)
520 vlist_delete(&bdev->ports, &bp->node);
521
522 return 0;
523 }
524
525 static int
526 bonding_hotplug_prepare(struct device *dev, struct device **bonding_dev)
527 {
528 struct bonding_device *bdev;
529
530 if (bonding_dev)
531 *bonding_dev = dev;
532
533 bdev = container_of(dev, struct bonding_device, dev);
534 bdev->force_active = true;
535 device_set_present(&bdev->dev, true);
536
537 return 0;
538 }
539
540 static void
541 bonding_retry_ports(struct uloop_timeout *timeout)
542 {
543 struct bonding_device *bdev = container_of(timeout, struct bonding_device, retry);
544 struct bonding_port *bp;
545
546 bdev->n_failed = 0;
547 vlist_for_each_element(&bdev->ports, bp, node) {
548 if (bp->present)
549 continue;
550
551 if (!bp->dev.dev->present)
552 continue;
553
554 bp->present = true;
555 bdev->n_present++;
556 bonding_enable_port(bp);
557 }
558 }
559
560
561 static void
562 bonding_free_port(struct bonding_port *bp)
563 {
564 struct device *dev = bp->dev.dev;
565
566 bonding_remove_port(bp);
567
568 device_lock();
569
570 device_remove_user(&bp->dev);
571
572 /*
573 * When reloading the config and moving a device from one master to
574 * another, the other master may have tried to claim this device
575 * before it was removed here.
576 * Ensure that claiming the device is retried by toggling its present
577 * state
578 */
579 if (dev->present) {
580 device_set_present(dev, false);
581 device_set_present(dev, true);
582 }
583
584 device_unlock();
585
586 free(bp);
587 }
588
589 static void
590 bonding_port_update(struct vlist_tree *tree, struct vlist_node *node_new,
591 struct vlist_node *node_old)
592 {
593 struct bonding_port *bp;
594 struct device *dev;
595
596 if (node_new) {
597 bp = container_of(node_new, struct bonding_port, node);
598
599 if (node_old) {
600 free(bp);
601 return;
602 }
603
604 dev = bp->dev.dev;
605 bp->dev.dev = NULL;
606 device_add_user(&bp->dev, dev);
607 }
608
609
610 if (node_old) {
611 bp = container_of(node_old, struct bonding_port, node);
612 bonding_free_port(bp);
613 }
614 }
615
616 static int
617 bonding_set_down(struct bonding_device *bdev)
618 {
619 struct bonding_port *bp;
620
621 bdev->set_state(&bdev->dev, false);
622
623 vlist_for_each_element(&bdev->ports, bp, node)
624 bonding_disable_port(bp, false);
625
626 bonding_set_active(bdev, false);
627
628 return 0;
629 }
630
631 static int
632 bonding_set_up(struct bonding_device *bdev)
633 {
634 struct bonding_port *bp;
635 int ret;
636
637 if (!bdev->n_present) {
638 if (!bdev->force_active)
639 return -ENOENT;
640
641 ret = bonding_set_active(bdev, true);
642 if (ret)
643 return ret;
644 }
645
646 bdev->n_failed = 0;
647 vlist_for_each_element(&bdev->ports, bp, node)
648 bonding_enable_port(bp);
649 if (bdev->n_failed)
650 uloop_timeout_set(&bdev->retry, 100);
651
652 if (!bdev->force_active && !bdev->n_present) {
653 /* initialization of all port interfaces failed */
654 bonding_set_active(bdev, false);
655 device_set_present(&bdev->dev, false);
656 return -ENOENT;
657 }
658
659 bonding_reset_primary(bdev);
660 ret = bdev->set_state(&bdev->dev, true);
661 if (ret < 0)
662 bonding_set_down(bdev);
663
664 return ret;
665 }
666
667 static int
668 bonding_set_state(struct device *dev, bool up)
669 {
670 struct bonding_device *bdev;
671
672 bdev = container_of(dev, struct bonding_device, dev);
673
674 if (up)
675 return bonding_set_up(bdev);
676 else
677 return bonding_set_down(bdev);
678 }
679
680 static struct device *
681 bonding_create(const char *name, struct device_type *devtype,
682 struct blob_attr *attr)
683 {
684 static const struct device_hotplug_ops bonding_ops = {
685 .prepare = bonding_hotplug_prepare,
686 .add = bonding_hotplug_add,
687 .del = bonding_hotplug_del
688 };
689 struct bonding_device *bdev;
690 struct device *dev = NULL;
691
692 bdev = calloc(1, sizeof(*bdev));
693 if (!bdev)
694 return NULL;
695
696 dev = &bdev->dev;
697
698 if (device_init(dev, devtype, name) < 0) {
699 device_cleanup(dev);
700 free(bdev);
701 return NULL;
702 }
703
704 dev->config_pending = true;
705 bdev->retry.cb = bonding_retry_ports;
706
707 bdev->set_state = dev->set_state;
708 dev->set_state = bonding_set_state;
709
710 dev->hotplug_ops = &bonding_ops;
711
712 vlist_init(&bdev->ports, avl_strcmp, bonding_port_update);
713 bdev->ports.keep_old = true;
714
715 bonding_reload(dev, attr);
716
717 return dev;
718 }
719
720 static void
721 bonding_free(struct device *dev)
722 {
723 struct bonding_device *bdev;
724
725 bdev = container_of(dev, struct bonding_device, dev);
726 vlist_flush_all(&bdev->ports);
727 free(bdev->config_data);
728 free(bdev);
729 }
730
731 static struct device_type bonding_device_type = {
732 .name = "bonding",
733 .config_params = &bonding_attr_list,
734
735 .bridge_capability = true,
736
737 .create = bonding_create,
738 .config_init = bonding_config_init,
739 .reload = bonding_reload,
740 .free = bonding_free,
741 };
742
743 static void __init bonding_device_type_init(void)
744 {
745 device_type_add(&bonding_device_type);
746 }