diff options
Diffstat (limited to 'net/bridge/br_if.c')
-rw-r--r-- | net/bridge/br_if.c | 121 |
1 files changed, 79 insertions, 42 deletions
diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index da687c8dc6ff..f36b35edd60c 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c | |||
@@ -79,37 +79,57 @@ static int port_cost(struct net_device *dev) | |||
79 | */ | 79 | */ |
80 | static void port_carrier_check(void *arg) | 80 | static void port_carrier_check(void *arg) |
81 | { | 81 | { |
82 | struct net_bridge_port *p = arg; | 82 | struct net_device *dev = arg; |
83 | struct net_bridge_port *p; | ||
84 | struct net_bridge *br; | ||
83 | 85 | ||
84 | rtnl_lock(); | 86 | rtnl_lock(); |
85 | if (netif_carrier_ok(p->dev)) { | 87 | p = dev->br_port; |
86 | u32 cost = port_cost(p->dev); | 88 | if (!p) |
89 | goto done; | ||
90 | br = p->br; | ||
87 | 91 | ||
88 | spin_lock_bh(&p->br->lock); | 92 | if (netif_carrier_ok(dev)) |
89 | if (p->state == BR_STATE_DISABLED) { | 93 | p->path_cost = port_cost(dev); |
90 | p->path_cost = cost; | 94 | |
91 | br_stp_enable_port(p); | 95 | if (br->dev->flags & IFF_UP) { |
96 | spin_lock_bh(&br->lock); | ||
97 | if (netif_carrier_ok(dev)) { | ||
98 | if (p->state == BR_STATE_DISABLED) | ||
99 | br_stp_enable_port(p); | ||
100 | } else { | ||
101 | if (p->state != BR_STATE_DISABLED) | ||
102 | br_stp_disable_port(p); | ||
92 | } | 103 | } |
93 | spin_unlock_bh(&p->br->lock); | 104 | spin_unlock_bh(&br->lock); |
94 | } else { | ||
95 | spin_lock_bh(&p->br->lock); | ||
96 | if (p->state != BR_STATE_DISABLED) | ||
97 | br_stp_disable_port(p); | ||
98 | spin_unlock_bh(&p->br->lock); | ||
99 | } | 105 | } |
106 | done: | ||
100 | rtnl_unlock(); | 107 | rtnl_unlock(); |
101 | } | 108 | } |
102 | 109 | ||
110 | static void release_nbp(struct kobject *kobj) | ||
111 | { | ||
112 | struct net_bridge_port *p | ||
113 | = container_of(kobj, struct net_bridge_port, kobj); | ||
114 | kfree(p); | ||
115 | } | ||
116 | |||
117 | static struct kobj_type brport_ktype = { | ||
118 | #ifdef CONFIG_SYSFS | ||
119 | .sysfs_ops = &brport_sysfs_ops, | ||
120 | #endif | ||
121 | .release = release_nbp, | ||
122 | }; | ||
123 | |||
103 | static void destroy_nbp(struct net_bridge_port *p) | 124 | static void destroy_nbp(struct net_bridge_port *p) |
104 | { | 125 | { |
105 | struct net_device *dev = p->dev; | 126 | struct net_device *dev = p->dev; |
106 | 127 | ||
107 | dev->br_port = NULL; | ||
108 | p->br = NULL; | 128 | p->br = NULL; |
109 | p->dev = NULL; | 129 | p->dev = NULL; |
110 | dev_put(dev); | 130 | dev_put(dev); |
111 | 131 | ||
112 | br_sysfs_freeif(p); | 132 | kobject_put(&p->kobj); |
113 | } | 133 | } |
114 | 134 | ||
115 | static void destroy_nbp_rcu(struct rcu_head *head) | 135 | static void destroy_nbp_rcu(struct rcu_head *head) |
@@ -133,24 +153,25 @@ static void del_nbp(struct net_bridge_port *p) | |||
133 | struct net_bridge *br = p->br; | 153 | struct net_bridge *br = p->br; |
134 | struct net_device *dev = p->dev; | 154 | struct net_device *dev = p->dev; |
135 | 155 | ||
136 | /* Race between RTNL notify and RCU callback */ | 156 | sysfs_remove_link(&br->ifobj, dev->name); |
137 | if (p->deleted) | ||
138 | return; | ||
139 | 157 | ||
140 | dev_set_promiscuity(dev, -1); | 158 | dev_set_promiscuity(dev, -1); |
141 | 159 | ||
142 | cancel_delayed_work(&p->carrier_check); | 160 | cancel_delayed_work(&p->carrier_check); |
143 | flush_scheduled_work(); | ||
144 | 161 | ||
145 | spin_lock_bh(&br->lock); | 162 | spin_lock_bh(&br->lock); |
146 | br_stp_disable_port(p); | 163 | br_stp_disable_port(p); |
147 | p->deleted = 1; | ||
148 | spin_unlock_bh(&br->lock); | 164 | spin_unlock_bh(&br->lock); |
149 | 165 | ||
150 | br_fdb_delete_by_port(br, p); | 166 | br_fdb_delete_by_port(br, p); |
151 | 167 | ||
152 | list_del_rcu(&p->list); | 168 | list_del_rcu(&p->list); |
153 | 169 | ||
170 | rcu_assign_pointer(dev->br_port, NULL); | ||
171 | |||
172 | kobject_uevent(&p->kobj, KOBJ_REMOVE); | ||
173 | kobject_del(&p->kobj); | ||
174 | |||
154 | call_rcu(&p->rcu, destroy_nbp_rcu); | 175 | call_rcu(&p->rcu, destroy_nbp_rcu); |
155 | } | 176 | } |
156 | 177 | ||
@@ -160,7 +181,6 @@ static void del_br(struct net_bridge *br) | |||
160 | struct net_bridge_port *p, *n; | 181 | struct net_bridge_port *p, *n; |
161 | 182 | ||
162 | list_for_each_entry_safe(p, n, &br->port_list, list) { | 183 | list_for_each_entry_safe(p, n, &br->port_list, list) { |
163 | br_sysfs_removeif(p); | ||
164 | del_nbp(p); | 184 | del_nbp(p); |
165 | } | 185 | } |
166 | 186 | ||
@@ -254,12 +274,17 @@ static struct net_bridge_port *new_nbp(struct net_bridge *br, | |||
254 | p->dev = dev; | 274 | p->dev = dev; |
255 | p->path_cost = port_cost(dev); | 275 | p->path_cost = port_cost(dev); |
256 | p->priority = 0x8000 >> BR_PORT_BITS; | 276 | p->priority = 0x8000 >> BR_PORT_BITS; |
257 | dev->br_port = p; | ||
258 | p->port_no = index; | 277 | p->port_no = index; |
259 | br_init_port(p); | 278 | br_init_port(p); |
260 | p->state = BR_STATE_DISABLED; | 279 | p->state = BR_STATE_DISABLED; |
261 | INIT_WORK(&p->carrier_check, port_carrier_check, p); | 280 | INIT_WORK(&p->carrier_check, port_carrier_check, dev); |
281 | br_stp_port_timer_init(p); | ||
282 | |||
262 | kobject_init(&p->kobj); | 283 | kobject_init(&p->kobj); |
284 | kobject_set_name(&p->kobj, SYSFS_BRIDGE_PORT_ATTR); | ||
285 | p->kobj.ktype = &brport_ktype; | ||
286 | p->kobj.parent = &(dev->class_dev.kobj); | ||
287 | p->kobj.kset = NULL; | ||
263 | 288 | ||
264 | return p; | 289 | return p; |
265 | } | 290 | } |
@@ -388,30 +413,43 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) | |||
388 | if (dev->br_port != NULL) | 413 | if (dev->br_port != NULL) |
389 | return -EBUSY; | 414 | return -EBUSY; |
390 | 415 | ||
391 | if (IS_ERR(p = new_nbp(br, dev))) | 416 | p = new_nbp(br, dev); |
417 | if (IS_ERR(p)) | ||
392 | return PTR_ERR(p); | 418 | return PTR_ERR(p); |
393 | 419 | ||
394 | if ((err = br_fdb_insert(br, p, dev->dev_addr))) | 420 | err = kobject_add(&p->kobj); |
395 | destroy_nbp(p); | 421 | if (err) |
396 | 422 | goto err0; | |
397 | else if ((err = br_sysfs_addif(p))) | ||
398 | del_nbp(p); | ||
399 | else { | ||
400 | dev_set_promiscuity(dev, 1); | ||
401 | 423 | ||
402 | list_add_rcu(&p->list, &br->port_list); | 424 | err = br_fdb_insert(br, p, dev->dev_addr); |
425 | if (err) | ||
426 | goto err1; | ||
403 | 427 | ||
404 | spin_lock_bh(&br->lock); | 428 | err = br_sysfs_addif(p); |
405 | br_stp_recalculate_bridge_id(br); | 429 | if (err) |
406 | br_features_recompute(br); | 430 | goto err2; |
407 | if ((br->dev->flags & IFF_UP) | ||
408 | && (dev->flags & IFF_UP) && netif_carrier_ok(dev)) | ||
409 | br_stp_enable_port(p); | ||
410 | spin_unlock_bh(&br->lock); | ||
411 | 431 | ||
412 | dev_set_mtu(br->dev, br_min_mtu(br)); | 432 | rcu_assign_pointer(dev->br_port, p); |
413 | } | 433 | dev_set_promiscuity(dev, 1); |
414 | 434 | ||
435 | list_add_rcu(&p->list, &br->port_list); | ||
436 | |||
437 | spin_lock_bh(&br->lock); | ||
438 | br_stp_recalculate_bridge_id(br); | ||
439 | br_features_recompute(br); | ||
440 | schedule_delayed_work(&p->carrier_check, BR_PORT_DEBOUNCE); | ||
441 | spin_unlock_bh(&br->lock); | ||
442 | |||
443 | dev_set_mtu(br->dev, br_min_mtu(br)); | ||
444 | kobject_uevent(&p->kobj, KOBJ_ADD); | ||
445 | |||
446 | return 0; | ||
447 | err2: | ||
448 | br_fdb_delete_by_port(br, p); | ||
449 | err1: | ||
450 | kobject_del(&p->kobj); | ||
451 | err0: | ||
452 | kobject_put(&p->kobj); | ||
415 | return err; | 453 | return err; |
416 | } | 454 | } |
417 | 455 | ||
@@ -423,7 +461,6 @@ int br_del_if(struct net_bridge *br, struct net_device *dev) | |||
423 | if (!p || p->br != br) | 461 | if (!p || p->br != br) |
424 | return -EINVAL; | 462 | return -EINVAL; |
425 | 463 | ||
426 | br_sysfs_removeif(p); | ||
427 | del_nbp(p); | 464 | del_nbp(p); |
428 | 465 | ||
429 | spin_lock_bh(&br->lock); | 466 | spin_lock_bh(&br->lock); |