diff options
author | David S. Miller <davem@davemloft.net> | 2013-01-04 13:32:04 -0800 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2013-01-04 13:33:14 -0800 |
commit | 912df2628bd1367d25d04860430f9179fbf144d2 (patch) | |
tree | f14aed911d388b41d4abde15605186a5f8e9509f | |
parent | 04e406dcc54cfd84d333ea673fa986fb5a1840e7 (diff) | |
parent | 85464ef271a0f5496f404c6a2f2dfbf1d76e1a49 (diff) | |
download | talos-op-linux-912df2628bd1367d25d04860430f9179fbf144d2.tar.gz talos-op-linux-912df2628bd1367d25d04860430f9179fbf144d2.zip |
net: introduce upper device lists and remove dev->master
Jiri Pirko says:
====================
This is a V6 of a repost of my previous patchset:
"[patch net-next v2 00/15] net: introduce upper device lists and remove dev->master" from Aug 14
The discussion around
"[net-next] bonding: don't allow the master to become its slave"
forced me to think about upper<->lower device connections.
This patchset adds a possibility to record upper device linkage.
All upper<->lower devices are converted to use this mechanism right after.
That leads to dev->master removal because this info becomes redundant since
"master links" have the same value.
After all changes, there is no longer possible to do things as:
"bond->someotherdevice->samebond"
Also I think that drivers like cxgb3, qlcnic, qeth would benefit by this
in future by being able to get more appropriate info about l3 addresses.
v5->v6:
- netdev_has_upper_dev() - added statement to comment that this is looking at
the immediate upper devices only.
- renamed "RTNL semaphore" -> "RTNL lock" in all comments
- renamed __netdev_has_upper_dev() to __netdev_search_upper_dev() to emhasize
the difference to netdev_has_upper_dev()
v4->v5:
- fixed missed typo in drivers/infiniband/hw/nes/nes_cm.c
v3->v4:
- comments in __netdev_upper_dev_link() squashed into one line
- kfree_rcu used instead of call_rcu in netdev_upper_dev_unlink()
v2->v3:
- removed recursion in __netdev_has_upper_dev()
- refreshed bits to be applicable on current net-next
v1->v2:
- s/unique/master/ better naming + stays closer to the past
- fixed vlan err goto
- original patch 15 (WARN_ON change) is squashed into the first patch
====================
Signed-off-by: David S. Miller <davem@davemloft.net>
-rw-r--r-- | drivers/infiniband/hw/nes/nes.c | 8 | ||||
-rw-r--r-- | drivers/infiniband/hw/nes/nes_cm.c | 2 | ||||
-rw-r--r-- | drivers/net/bonding/bond_3ad.c | 30 | ||||
-rw-r--r-- | drivers/net/bonding/bond_alb.c | 6 | ||||
-rw-r--r-- | drivers/net/bonding/bond_main.c | 94 | ||||
-rw-r--r-- | drivers/net/bonding/bonding.h | 14 | ||||
-rw-r--r-- | drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c | 11 | ||||
-rw-r--r-- | drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c | 2 | ||||
-rw-r--r-- | drivers/net/macvlan.c | 9 | ||||
-rw-r--r-- | drivers/net/team/team.c | 13 | ||||
-rw-r--r-- | drivers/s390/net/qeth_l3_main.c | 21 | ||||
-rw-r--r-- | include/linux/netdevice.h | 22 | ||||
-rw-r--r-- | net/8021q/vlan.c | 10 | ||||
-rw-r--r-- | net/8021q/vlan_core.c | 18 | ||||
-rw-r--r-- | net/bridge/br_if.c | 6 | ||||
-rw-r--r-- | net/core/dev.c | 246 | ||||
-rw-r--r-- | net/core/netpoll.c | 9 | ||||
-rw-r--r-- | net/core/rtnetlink.c | 70 |
18 files changed, 409 insertions, 182 deletions
diff --git a/drivers/infiniband/hw/nes/nes.c b/drivers/infiniband/hw/nes/nes.c index 748db2d3e465..d129c367ec16 100644 --- a/drivers/infiniband/hw/nes/nes.c +++ b/drivers/infiniband/hw/nes/nes.c @@ -135,6 +135,7 @@ static int nes_inetaddr_event(struct notifier_block *notifier, struct net_device *event_netdev = ifa->ifa_dev->dev; struct nes_device *nesdev; struct net_device *netdev; + struct net_device *upper_dev; struct nes_vnic *nesvnic; unsigned int is_bonded; @@ -145,8 +146,9 @@ static int nes_inetaddr_event(struct notifier_block *notifier, nesdev, nesdev->netdev[0]->name); netdev = nesdev->netdev[0]; nesvnic = netdev_priv(netdev); + upper_dev = netdev_master_upper_dev_get(netdev); is_bonded = netif_is_bond_slave(netdev) && - (netdev->master == event_netdev); + (upper_dev == event_netdev); if ((netdev == event_netdev) || is_bonded) { if (nesvnic->rdma_enabled == 0) { nes_debug(NES_DBG_NETDEV, "Returning without processing event for %s since" @@ -179,9 +181,9 @@ static int nes_inetaddr_event(struct notifier_block *notifier, /* fall through */ case NETDEV_CHANGEADDR: /* Add the address to the IP table */ - if (netdev->master) + if (upper_dev) nesvnic->local_ipaddr = - ((struct in_device *)netdev->master->ip_ptr)->ifa_list->ifa_address; + ((struct in_device *)upper_dev->ip_ptr)->ifa_list->ifa_address; else nesvnic->local_ipaddr = ifa->ifa_address; diff --git a/drivers/infiniband/hw/nes/nes_cm.c b/drivers/infiniband/hw/nes/nes_cm.c index 22ea67eea5dc..24b9f1a0107b 100644 --- a/drivers/infiniband/hw/nes/nes_cm.c +++ b/drivers/infiniband/hw/nes/nes_cm.c @@ -1340,7 +1340,7 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi } if (netif_is_bond_slave(nesvnic->netdev)) - netdev = nesvnic->netdev->master; + netdev = netdev_master_upper_dev_get(nesvnic->netdev); else netdev = nesvnic->netdev; diff --git a/drivers/net/bonding/bond_3ad.c b/drivers/net/bonding/bond_3ad.c index a030e635f001..84fabd69df4c 100644 --- a/drivers/net/bonding/bond_3ad.c +++ b/drivers/net/bonding/bond_3ad.c @@ -1127,7 +1127,7 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) // INFO_RECEIVED_LOOPBACK_FRAMES pr_err("%s: An illegal loopback occurred on adapter (%s).\n" "Check the configuration to verify that all adapters are connected to 802.3ad compliant switch ports\n", - port->slave->dev->master->name, port->slave->dev->name); + port->slave->bond->dev->name, port->slave->dev->name); return; } __update_selected(lacpdu, port); @@ -1306,7 +1306,7 @@ static void ad_port_selection_logic(struct port *port) } if (!curr_port) { // meaning: the port was related to an aggregator but was not on the aggregator port list pr_warning("%s: Warning: Port %d (on %s) was related to aggregator %d but was not on its port list\n", - port->slave->dev->master->name, + port->slave->bond->dev->name, port->actor_port_number, port->slave->dev->name, port->aggregator->aggregator_identifier); @@ -1386,7 +1386,7 @@ static void ad_port_selection_logic(struct port *port) port->aggregator->aggregator_identifier); } else { pr_err("%s: Port %d (on %s) did not find a suitable aggregator\n", - port->slave->dev->master->name, + port->slave->bond->dev->name, port->actor_port_number, port->slave->dev->name); } } @@ -1463,7 +1463,7 @@ static struct aggregator *ad_agg_selection_test(struct aggregator *best, default: pr_warning("%s: Impossible agg select mode %d\n", - curr->slave->dev->master->name, + curr->slave->bond->dev->name, __get_agg_selection_mode(curr->lag_ports)); break; } @@ -1571,7 +1571,7 @@ static void ad_agg_selection_logic(struct aggregator *agg) // check if any partner replys if (best->is_individual) { pr_warning("%s: Warning: No 802.3ad response from the link partner for any adapters in the bond\n", - best->slave ? best->slave->dev->master->name : "NULL"); + best->slave ? best->slave->bond->dev->name : "NULL"); } best->is_active = 1; @@ -1898,7 +1898,7 @@ int bond_3ad_bind_slave(struct slave *slave) if (bond == NULL) { pr_err("%s: The slave %s is not attached to its bond\n", - slave->dev->master->name, slave->dev->name); + slave->bond->dev->name, slave->dev->name); return -1; } @@ -1973,7 +1973,7 @@ void bond_3ad_unbind_slave(struct slave *slave) // if slave is null, the whole port is not initialized if (!port->slave) { pr_warning("Warning: %s: Trying to unbind an uninitialized port on %s\n", - slave->dev->master->name, slave->dev->name); + slave->bond->dev->name, slave->dev->name); return; } @@ -2009,7 +2009,7 @@ void bond_3ad_unbind_slave(struct slave *slave) if ((new_aggregator->lag_ports == port) && new_aggregator->is_active) { pr_info("%s: Removing an active aggregator\n", - aggregator->slave->dev->master->name); + aggregator->slave->bond->dev->name); // select new active aggregator select_new_active_agg = 1; } @@ -2040,7 +2040,7 @@ void bond_3ad_unbind_slave(struct slave *slave) ad_agg_selection_logic(__get_first_agg(port)); } else { pr_warning("%s: Warning: unbinding aggregator, and could not find a new aggregator for its ports\n", - slave->dev->master->name); + slave->bond->dev->name); } } else { // in case that the only port related to this aggregator is the one we want to remove select_new_active_agg = aggregator->is_active; @@ -2048,7 +2048,7 @@ void bond_3ad_unbind_slave(struct slave *slave) ad_clear_agg(aggregator); if (select_new_active_agg) { pr_info("%s: Removing an active aggregator\n", - slave->dev->master->name); + slave->bond->dev->name); // select new active aggregator ad_agg_selection_logic(__get_first_agg(port)); } @@ -2076,7 +2076,7 @@ void bond_3ad_unbind_slave(struct slave *slave) ad_clear_agg(temp_aggregator); if (select_new_active_agg) { pr_info("%s: Removing an active aggregator\n", - slave->dev->master->name); + slave->bond->dev->name); // select new active aggregator ad_agg_selection_logic(__get_first_agg(port)); } @@ -2184,7 +2184,7 @@ static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u1 if (!port->slave) { pr_warning("%s: Warning: port of slave %s is uninitialized\n", - slave->dev->name, slave->dev->master->name); + slave->dev->name, slave->bond->dev->name); return ret; } @@ -2240,7 +2240,7 @@ void bond_3ad_adapter_speed_changed(struct slave *slave) // if slave is null, the whole port is not initialized if (!port->slave) { pr_warning("Warning: %s: speed changed for uninitialized port on %s\n", - slave->dev->master->name, slave->dev->name); + slave->bond->dev->name, slave->dev->name); return; } @@ -2268,7 +2268,7 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave) // if slave is null, the whole port is not initialized if (!port->slave) { pr_warning("%s: Warning: duplex changed for uninitialized port on %s\n", - slave->dev->master->name, slave->dev->name); + slave->bond->dev->name, slave->dev->name); return; } @@ -2297,7 +2297,7 @@ void bond_3ad_handle_link_change(struct slave *slave, char link) // if slave is null, the whole port is not initialized if (!port->slave) { pr_warning("Warning: %s: link status changed for uninitialized port on %s\n", - slave->dev->master->name, slave->dev->name); + slave->bond->dev->name, slave->dev->name); return; } diff --git a/drivers/net/bonding/bond_alb.c b/drivers/net/bonding/bond_alb.c index 7c9d136e74be..f5e052723029 100644 --- a/drivers/net/bonding/bond_alb.c +++ b/drivers/net/bonding/bond_alb.c @@ -507,7 +507,7 @@ static void rlb_update_client(struct rlb_client_info *client_info) client_info->mac_dst); if (!skb) { pr_err("%s: Error: failed to create an ARP packet\n", - client_info->slave->dev->master->name); + client_info->slave->bond->dev->name); continue; } @@ -517,7 +517,7 @@ static void rlb_update_client(struct rlb_client_info *client_info) skb = vlan_put_tag(skb, client_info->vlan_id); if (!skb) { pr_err("%s: Error: failed to insert VLAN tag\n", - client_info->slave->dev->master->name); + client_info->slave->bond->dev->name); continue; } } @@ -1043,7 +1043,7 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[]) if (dev_set_mac_address(dev, &s_addr)) { pr_err("%s: Error: dev_set_mac_address of dev %s failed!\n" "ALB mode requires that the base driver support setting the hw address also when the network device's interface is open\n", - dev->master->name, dev->name); + slave->bond->dev->name, dev->name); return -EOPNOTSUPP; } return 0; diff --git a/drivers/net/bonding/bond_main.c b/drivers/net/bonding/bond_main.c index b7d45f367d4a..c4952985c084 100644 --- a/drivers/net/bonding/bond_main.c +++ b/drivers/net/bonding/bond_main.c @@ -746,11 +746,9 @@ static void __bond_resend_igmp_join_requests(struct net_device *dev) { struct in_device *in_dev; - rcu_read_lock(); in_dev = __in_dev_get_rcu(dev); if (in_dev) ip_mc_rejoin_groups(in_dev); - rcu_read_unlock(); } /* @@ -760,9 +758,10 @@ static void __bond_resend_igmp_join_requests(struct net_device *dev) */ static void bond_resend_igmp_join_requests(struct bonding *bond) { - struct net_device *bond_dev, *vlan_dev, *master_dev; + struct net_device *bond_dev, *vlan_dev, *upper_dev; struct vlan_entry *vlan; + rcu_read_lock(); read_lock(&bond->lock); bond_dev = bond->dev; @@ -774,18 +773,14 @@ static void bond_resend_igmp_join_requests(struct bonding *bond) * if bond is enslaved to a bridge, * then rejoin all groups on its master */ - master_dev = bond_dev->master; - if (master_dev) - if ((master_dev->priv_flags & IFF_EBRIDGE) - && (bond_dev->priv_flags & IFF_BRIDGE_PORT)) - __bond_resend_igmp_join_requests(master_dev); + upper_dev = netdev_master_upper_dev_get_rcu(bond_dev); + if (upper_dev && upper_dev->priv_flags & IFF_EBRIDGE) + __bond_resend_igmp_join_requests(upper_dev); /* rejoin all groups on vlan devices */ list_for_each_entry(vlan, &bond->vlan_list, vlan_list) { - rcu_read_lock(); vlan_dev = __vlan_find_dev_deep(bond_dev, vlan->vlan_id); - rcu_read_unlock(); if (vlan_dev) __bond_resend_igmp_join_requests(vlan_dev); } @@ -794,13 +789,16 @@ static void bond_resend_igmp_join_requests(struct bonding *bond) queue_delayed_work(bond->wq, &bond->mcast_work, HZ/5); read_unlock(&bond->lock); + rcu_read_unlock(); } static void bond_resend_igmp_join_requests_delayed(struct work_struct *work) { struct bonding *bond = container_of(work, struct bonding, mcast_work.work); + rcu_read_lock(); bond_resend_igmp_join_requests(bond); + rcu_read_unlock(); } /* @@ -1493,6 +1491,27 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb) return ret; } +static int bond_master_upper_dev_link(struct net_device *bond_dev, + struct net_device *slave_dev) +{ + int err; + + err = netdev_master_upper_dev_link(slave_dev, bond_dev); + if (err) + return err; + slave_dev->flags |= IFF_SLAVE; + rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE); + return 0; +} + +static void bond_upper_dev_unlink(struct net_device *bond_dev, + struct net_device *slave_dev) +{ + netdev_upper_dev_unlink(slave_dev, bond_dev); + slave_dev->flags &= ~IFF_SLAVE; + rtmsg_ifinfo(RTM_NEWLINK, slave_dev, IFF_SLAVE); +} + /* enslave device <slave> to bond device <master> */ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) { @@ -1655,9 +1674,9 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) } } - res = netdev_set_bond_master(slave_dev, bond_dev); + res = bond_master_upper_dev_link(bond_dev, slave_dev); if (res) { - pr_debug("Error %d calling netdev_set_bond_master\n", res); + pr_debug("Error %d calling bond_master_upper_dev_link\n", res); goto err_restore_mac; } @@ -1891,7 +1910,7 @@ err_close: dev_close(slave_dev); err_unset_master: - netdev_set_bond_master(slave_dev, NULL); + bond_upper_dev_unlink(bond_dev, slave_dev); err_restore_mac: if (!bond->params.fail_over_mac) { @@ -1936,7 +1955,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev) /* slave is not a slave or master is not master of this slave */ if (!(slave_dev->flags & IFF_SLAVE) || - (slave_dev->master != bond_dev)) { + !netdev_has_upper_dev(slave_dev, bond_dev)) { pr_err("%s: Error: cannot release %s.\n", bond_dev->name, slave_dev->name); return -EINVAL; @@ -2080,7 +2099,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev) netif_addr_unlock_bh(bond_dev); } - netdev_set_bond_master(slave_dev, NULL); + bond_upper_dev_unlink(bond_dev, slave_dev); slave_disable_netpoll(slave); @@ -2195,7 +2214,7 @@ static int bond_release_all(struct net_device *bond_dev) netif_addr_unlock_bh(bond_dev); } - netdev_set_bond_master(slave_dev, NULL); + bond_upper_dev_unlink(bond_dev, slave_dev); slave_disable_netpoll(slave); @@ -2259,8 +2278,9 @@ static int bond_ioctl_change_active(struct net_device *bond_dev, struct net_devi if (!USES_PRIMARY(bond->params.mode)) return -EINVAL; - /* Verify that master_dev is indeed the master of slave_dev */ - if (!(slave_dev->flags & IFF_SLAVE) || (slave_dev->master != bond_dev)) + /* Verify that bond_dev is indeed the master of slave_dev */ + if (!(slave_dev->flags & IFF_SLAVE) || + !netdev_has_upper_dev(slave_dev, bond_dev)) return -EINVAL; read_lock(&bond->lock); @@ -3258,36 +3278,32 @@ static int bond_master_netdev_event(unsigned long event, static int bond_slave_netdev_event(unsigned long event, struct net_device *slave_dev) { - struct net_device *bond_dev = slave_dev->master; - struct bonding *bond = netdev_priv(bond_dev); - struct slave *slave = NULL; + struct slave *slave = bond_slave_get_rtnl(slave_dev); + struct bonding *bond = slave->bond; + struct net_device *bond_dev = slave->bond->dev; + u32 old_speed; + u8 old_duplex; switch (event) { case NETDEV_UNREGISTER: - if (bond_dev) { - if (bond->setup_by_slave) - bond_release_and_destroy(bond_dev, slave_dev); - else - bond_release(bond_dev, slave_dev); - } + if (bond->setup_by_slave) + bond_release_and_destroy(bond_dev, slave_dev); + else + bond_release(bond_dev, slave_dev); break; case NETDEV_UP: case NETDEV_CHANGE: - slave = bond_get_slave_by_dev(bond, slave_dev); - if (slave) { - u32 old_speed = slave->speed; - u8 old_duplex = slave->duplex; + old_speed = slave->speed; + old_duplex = slave->duplex; - bond_update_speed_duplex(slave); + bond_update_speed_duplex(slave); - if (bond->params.mode == BOND_MODE_8023AD) { - if (old_speed != slave->speed) - bond_3ad_adapter_speed_changed(slave); - if (old_duplex != slave->duplex) - bond_3ad_adapter_duplex_changed(slave); - } + if (bond->params.mode == BOND_MODE_8023AD) { + if (old_speed != slave->speed) + bond_3ad_adapter_speed_changed(slave); + if (old_duplex != slave->duplex) + bond_3ad_adapter_duplex_changed(slave); } - break; case NETDEV_DOWN: /* diff --git a/drivers/net/bonding/bonding.h b/drivers/net/bonding/bonding.h index 21b68e5c14fd..0d282d20b5a8 100644 --- a/drivers/net/bonding/bonding.h +++ b/drivers/net/bonding/bonding.h @@ -258,6 +258,9 @@ static inline bool bond_vlan_used(struct bonding *bond) #define bond_slave_get_rcu(dev) \ ((struct slave *) rcu_dereference(dev->rx_handler_data)) +#define bond_slave_get_rtnl(dev) \ + ((struct slave *) rtnl_dereference(dev->rx_handler_data)) + /** * Returns NULL if the net_device does not belong to any of the bond's slaves * @@ -280,11 +283,9 @@ static inline struct slave *bond_get_slave_by_dev(struct bonding *bond, static inline struct bonding *bond_get_bond_by_slave(struct slave *slave) { - if (!slave || !slave->dev->master) { + if (!slave || !slave->bond) return NULL; - } - - return netdev_priv(slave->dev->master); + return slave->bond; } static inline bool bond_is_lb(const struct bonding *bond) @@ -360,10 +361,9 @@ static inline void bond_netpoll_send_skb(const struct slave *slave, static inline void bond_set_slave_inactive_flags(struct slave *slave) { - struct bonding *bond = netdev_priv(slave->dev->master); - if (!bond_is_lb(bond)) + if (!bond_is_lb(slave->bond)) bond_set_backup_slave(slave); - if (!bond->params.all_slaves_active) + if (!slave->bond->params.all_slaves_active) slave->inactive = 1; } diff --git a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c index 942dace361d2..3f1f5018c66e 100644 --- a/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c +++ b/drivers/net/ethernet/chelsio/cxgb3/cxgb3_offload.c @@ -182,14 +182,17 @@ static struct net_device *get_iff_from_mac(struct adapter *adapter, struct net_device *dev = adapter->port[i]; if (!memcmp(dev->dev_addr, mac, ETH_ALEN)) { + rcu_read_lock(); if (vlan && vlan != VLAN_VID_MASK) { - rcu_read_lock(); dev = __vlan_find_dev_deep(dev, vlan); - rcu_read_unlock(); } else if (netif_is_bond_slave(dev)) { - while (dev->master) - dev = dev->master; + struct net_device *upper_dev; + + while ((upper_dev = + netdev_master_upper_dev_get_rcu(dev))) + dev = upper_dev; } + rcu_read_unlock(); return dev; } } diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 137ca13710af..3655ca2de525 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -3186,12 +3186,14 @@ void qlcnic_restore_indev_addr(struct net_device *netdev, unsigned long event) qlcnic_config_indev_addr(adapter, netdev, event); + rcu_read_lock(); for_each_set_bit(vid, adapter->vlans, VLAN_N_VID) { dev = __vlan_find_dev_deep(netdev, vid); if (!dev) continue; qlcnic_config_indev_addr(adapter, dev, event); } + rcu_read_unlock(); } static int qlcnic_netdev_event(struct notifier_block *this, diff --git a/drivers/net/macvlan.c b/drivers/net/macvlan.c index 80c6f927a7b4..0ae45182f40d 100644 --- a/drivers/net/macvlan.c +++ b/drivers/net/macvlan.c @@ -764,16 +764,22 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, memcpy(dev->dev_addr, lowerdev->dev_addr, ETH_ALEN); } + err = netdev_upper_dev_link(lowerdev, dev); + if (err) + goto destroy_port; + port->count += 1; err = register_netdevice(dev); if (err < 0) - goto destroy_port; + goto upper_dev_unlink; list_add_tail(&vlan->list, &port->vlans); netif_stacked_transfer_operstate(lowerdev, dev); return 0; +upper_dev_unlink: + netdev_upper_dev_unlink(lowerdev, dev); destroy_port: port->count -= 1; if (!port->count) @@ -797,6 +803,7 @@ void macvlan_dellink(struct net_device *dev, struct list_head *head) list_del(&vlan->list); unregister_netdevice_queue(dev, head); + netdev_upper_dev_unlink(vlan->lowerdev, dev); } EXPORT_SYMBOL_GPL(macvlan_dellink); diff --git a/drivers/net/team/team.c b/drivers/net/team/team.c index 2ea4f0ec54b0..250f8c1df72c 100644 --- a/drivers/net/team/team.c +++ b/drivers/net/team/team.c @@ -1055,10 +1055,11 @@ static int team_port_add(struct team *team, struct net_device *port_dev) } } - err = netdev_set_master(port_dev, dev); + err = netdev_master_upper_dev_link(port_dev, dev); if (err) { - netdev_err(dev, "Device %s failed to set master\n", portname); - goto err_set_master; + netdev_err(dev, "Device %s failed to set upper link\n", + portname); + goto err_set_upper_link; } err = netdev_rx_handler_register(port_dev, team_handle_frame, @@ -1091,9 +1092,9 @@ err_option_port_add: netdev_rx_handler_unregister(port_dev); err_handler_register: - netdev_set_master(port_dev, NULL); + netdev_upper_dev_unlink(port_dev, dev); -err_set_master: +err_set_upper_link: team_port_disable_netpoll(port); err_enable_netpoll: @@ -1137,7 +1138,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev) team_port_disable(team, port); list_del_rcu(&port->list); netdev_rx_handler_unregister(port_dev); - netdev_set_master(port_dev, NULL); + netdev_upper_dev_unlink(port_dev, dev); team_port_disable_netpoll(port); vlan_vids_del_by_dev(port_dev, dev); dev_close(port_dev); diff --git a/drivers/s390/net/qeth_l3_main.c b/drivers/s390/net/qeth_l3_main.c index 6e5eef01e667..0749efe1c6d9 100644 --- a/drivers/s390/net/qeth_l3_main.c +++ b/drivers/s390/net/qeth_l3_main.c @@ -1640,6 +1640,7 @@ static void qeth_l3_add_mc(struct qeth_card *card, struct in_device *in4_dev) } } +/* called with rcu_read_lock */ static void qeth_l3_add_vlan_mc(struct qeth_card *card) { struct in_device *in_dev; @@ -1652,19 +1653,14 @@ static void qeth_l3_add_vlan_mc(struct qeth_card *card) for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) { struct net_device *netdev; - rcu_read_lock(); netdev = __vlan_find_dev_deep(card->dev, vid); - rcu_read_unlock(); if (netdev == NULL || !(netdev->flags & IFF_UP)) continue; - in_dev = in_dev_get(netdev); + in_dev = __in_dev_get_rcu(netdev); if (!in_dev) continue; - rcu_read_lock(); qeth_l3_add_mc(card, in_dev); - rcu_read_unlock(); - in_dev_put(in_dev); } } @@ -1673,14 +1669,14 @@ static void qeth_l3_add_multicast_ipv4(struct qeth_card *card) struct in_device *in4_dev; QETH_CARD_TEXT(card, 4, "chkmcv4"); - in4_dev = in_dev_get(card->dev); - if (in4_dev == NULL) - return; rcu_read_lock(); + in4_dev = __in_dev_get_rcu(card->dev); + if (in4_dev == NULL) + goto unlock; qeth_l3_add_mc(card, in4_dev); qeth_l3_add_vlan_mc(card); +unlock: rcu_read_unlock(); - in_dev_put(in4_dev); } #ifdef CONFIG_QETH_IPV6 @@ -1705,6 +1701,7 @@ static void qeth_l3_add_mc6(struct qeth_card *card, struct inet6_dev *in6_dev) } } +/* called with rcu_read_lock */ static void qeth_l3_add_vlan_mc6(struct qeth_card *card) { struct inet6_dev *in_dev; @@ -1741,10 +1738,12 @@ static void qeth_l3_add_multicast_ipv6(struct qeth_card *card) in6_dev = in6_dev_get(card->dev); if (in6_dev == NULL) return; + rcu_read_lock(); read_lock_bh(&in6_dev->lock); qeth_l3_add_mc6(card, in6_dev); qeth_l3_add_vlan_mc6(card); read_unlock_bh(&in6_dev->lock); + rcu_read_unlock(); in6_dev_put(in6_dev); } #endif /* CONFIG_QETH_IPV6 */ @@ -1813,8 +1812,10 @@ static void qeth_l3_free_vlan_addresses6(struct qeth_card *card, static void qeth_l3_free_vlan_addresses(struct qeth_card *card, unsigned short vid) { + rcu_read_lock(); qeth_l3_free_vlan_addresses4(card, vid); qeth_l3_free_vlan_addresses6(card, vid); + rcu_read_unlock(); } static int qeth_l3_vlan_rx_add_vid(struct net_device *dev, unsigned short vid) diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index c5031a45e185..0209ac328e8a 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h @@ -858,8 +858,7 @@ struct netdev_fcoe_hbainfo { * flow_id is a flow ID to be passed to rps_may_expire_flow() later. * Return the filter ID on success, or a negative error code. * - * Slave management functions (for bridge, bonding, etc). User should - * call netdev_set_master() to set dev->master properly. + * Slave management functions (for bridge, bonding, etc). * int (*ndo_add_slave)(struct net_device *dev, struct net_device *slave_dev); * Called to make another netdev an underling. * @@ -1170,9 +1169,7 @@ struct net_device { * avoid dirtying this cache line. */ - struct net_device *master; /* Pointer to master device of a group, - * which this device is member of. - */ + struct list_head upper_dev_list; /* List of upper devices */ /* Interface address info used in eth_type_trans() */ unsigned char *dev_addr; /* hw address, (before bcast @@ -2636,9 +2633,18 @@ extern int netdev_max_backlog; extern int netdev_tstamp_prequeue; extern int weight_p; extern int bpf_jit_enable; -extern int netdev_set_master(struct net_device *dev, struct net_device *master); -extern int netdev_set_bond_master(struct net_device *dev, - struct net_device *master); + +extern bool netdev_has_upper_dev(struct net_device *dev, + struct net_device *upper_dev); +extern bool netdev_has_any_upper_dev(struct net_device *dev); +extern struct net_device *netdev_master_upper_dev_get(struct net_device *dev); +extern struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev); +extern int netdev_upper_dev_link(struct net_device *dev, + struct net_device *upper_dev); +extern int netdev_master_upper_dev_link(struct net_device *dev, + struct net_device *upper_dev); +extern void netdev_upper_dev_unlink(struct net_device *dev, + struct net_device *upper_dev); extern int skb_checksum_help(struct sk_buff *skb); extern struct sk_buff *skb_gso_segment(struct sk_buff *skb, netdev_features_t features); diff --git a/net/8021q/vlan.c b/net/8021q/vlan.c index a292e8050ef2..babfde9f734c 100644 --- a/net/8021q/vlan.c +++ b/net/8021q/vlan.c @@ -105,6 +105,8 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) */ unregister_netdevice_queue(dev, head); + netdev_upper_dev_unlink(real_dev, dev); + if (grp->nr_vlan_devs == 0) vlan_gvrp_uninit_applicant(real_dev); @@ -162,9 +164,13 @@ int register_vlan_dev(struct net_device *dev) if (err < 0) goto out_uninit_applicant; + err = netdev_upper_dev_link(real_dev, dev); + if (err) + goto out_uninit_applicant; + err = register_netdevice(dev); if (err < 0) - goto out_uninit_applicant; + goto out_upper_dev_unlink; /* Account for reference in struct vlan_dev_priv */ dev_hold(real_dev); @@ -180,6 +186,8 @@ int register_vlan_dev(struct net_device *dev) return 0; +out_upper_dev_unlink: + netdev_upper_dev_unlink(real_dev, dev); out_uninit_applicant: if (grp->nr_vlan_devs == 0) vlan_gvrp_uninit_applicant(real_dev); diff --git a/net/8021q/vlan_core.c b/net/8021q/vlan_core.c index 65e06abe023f..380440b8ea89 100644 --- a/net/8021q/vlan_core.c +++ b/net/8021q/vlan_core.c @@ -60,21 +60,25 @@ bool vlan_do_receive(struct sk_buff **skbp) return true; } -/* Must be invoked with rcu_read_lock or with RTNL. */ -struct net_device *__vlan_find_dev_deep(struct net_device *real_dev, +/* Must be invoked with rcu_read_lock. */ +struct net_device *__vlan_find_dev_deep(struct net_device *dev, u16 vlan_id) { - struct vlan_info *vlan_info = rcu_dereference_rtnl(real_dev->vlan_info); + struct vlan_info *vlan_info = rcu_dereference(dev->vlan_info); if (vlan_info) { return vlan_group_get_device(&vlan_info->grp, vlan_id); } else { /* - * Bonding slaves do not have grp assigned to themselves. - * Grp is assigned to bonding master instead. + * Lower devices of master uppers (bonding, team) do not have + * grp assigned to themselves. Grp is assigned to upper device + * instead. */ - if (netif_is_bond_slave(real_dev)) - return __vlan_find_dev_deep(real_dev->master, vlan_id); + struct net_device *upper_dev; + + upper_dev = netdev_master_upper_dev_get_rcu(dev); + if (upper_dev) + return __vlan_find_dev_deep(upper_dev, vlan_id); } return NULL; diff --git a/net/bridge/br_if.c b/net/bridge/br_if.c index 1edd71d44a4c..2148d474a04f 100644 --- a/net/bridge/br_if.c +++ b/net/bridge/br_if.c @@ -148,7 +148,7 @@ static void del_nbp(struct net_bridge_port *p) netdev_rx_handler_unregister(dev); synchronize_net(); - netdev_set_master(dev, NULL); + netdev_upper_dev_unlink(dev, br->dev); br_multicast_del_port(p); @@ -364,7 +364,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) if (br_netpoll_info(br) && ((err = br_netpoll_enable(p, GFP_KERNEL)))) goto err3; - err = netdev_set_master(dev, br->dev); + err = netdev_master_upper_dev_link(dev, br->dev); if (err) goto err4; @@ -403,7 +403,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) return 0; err5: - netdev_set_master(dev, NULL); + netdev_upper_dev_unlink(dev, br->dev); err4: br_netpoll_disable(p); err3: diff --git a/net/core/dev.c b/net/core/dev.c index bddb2f2ccaa9..a51ccf46e8b7 100644 --- a/net/core/dev.c +++ b/net/core/dev.c @@ -4600,64 +4600,231 @@ static int __init dev_proc_init(void) #endif /* CONFIG_PROC_FS */ +struct netdev_upper { + struct net_device *dev; + bool master; + struct list_head list; + struct rcu_head rcu; + struct list_head search_list; +}; + +static void __append_search_uppers(struct list_head *search_list, + struct net_device *dev) +{ + struct netdev_upper *upper; + + list_for_each_entry(upper, &dev->upper_dev_list, list) { + /* check if this upper is not already in search list */ + if (list_empty(&upper->search_list)) + list_add_tail(&upper->search_list, search_list); + } +} + +static bool __netdev_search_upper_dev(struct net_device *dev, + struct net_device *upper_dev) +{ + LIST_HEAD(search_list); + struct netdev_upper *upper; + struct netdev_upper *tmp; + bool ret = false; + + __append_search_uppers(&search_list, dev); + list_for_each_entry(upper, &search_list, search_list) { + if (upper->dev == upper_dev) { + ret = true; + break; + } + __append_search_uppers(&search_list, upper->dev); + } + list_for_each_entry_safe(upper, tmp, &search_list, search_list) + INIT_LIST_HEAD(&upper->search_list); + return ret; +} + +static struct netdev_upper *__netdev_find_upper(struct net_device *dev, + struct net_device *upper_dev) +{ + struct netdev_upper *upper; + + list_for_each_entry(upper, &dev->upper_dev_list, list) { + if (upper->dev == upper_dev) + return upper; + } + return NULL; +} + /** - * netdev_set_master - set up master pointer - * @slave: slave device - * @master: new master device - * - * Changes the master device of the slave. Pass %NULL to break the - * bonding. The caller must hold the RTNL semaphore. On a failure - * a negative errno code is returned. On success the reference counts - * are adjusted and the function returns zero. + * netdev_has_upper_dev - Check if device is linked to an upper device + * @dev: device + * @upper_dev: upper device to check + * + * Find out if a device is linked to specified upper device and return true + * in case it is. Note that this checks only immediate upper device, + * not through a complete stack of devices. The caller must hold the RTNL lock. */ -int netdev_set_master(struct net_device *slave, struct net_device *master) +bool netdev_has_upper_dev(struct net_device *dev, + struct net_device *upper_dev) { - struct net_device *old = slave->master; + ASSERT_RTNL(); + return __netdev_find_upper(dev, upper_dev); +} +EXPORT_SYMBOL(netdev_has_upper_dev); + +/** + * netdev_has_any_upper_dev - Check if device is linked to some device + * @dev: device + * + * Find out if a device is linked to an upper device and return true in case + * it is. The caller must hold the RTNL lock. + */ +bool netdev_has_any_upper_dev(struct net_device *dev) +{ ASSERT_RTNL(); - if (master) { - if (old) - return -EBUSY; - dev_hold(master); - } + return !list_empty(&dev->upper_dev_list); +} +EXPORT_SYMBOL(netdev_has_any_upper_dev); - slave->master = master; +/** + * netdev_master_upper_dev_get - Get master upper device + * @dev: device + * + * Find a master upper device and return pointer to it or NULL in case + * it's not there. The caller must hold the RTNL lock. + */ +struct net_device *netdev_master_upper_dev_get(struct net_device *dev) +{ + struct netdev_upper *upper; - if (old) - dev_put(old); - return 0; + ASSERT_RTNL(); + + if (list_empty(&dev->upper_dev_list)) + return NULL; + + upper = list_first_entry(&dev->upper_dev_list, + struct netdev_upper, list); + if (likely(upper->master)) + return upper->dev; + return NULL; } -EXPORT_SYMBOL(netdev_set_master); +EXPORT_SYMBOL(netdev_master_upper_dev_get); /** - * netdev_set_bond_master - set up bonding master/slave pair - * @slave: slave device - * @master: new master device - * - * Changes the master device of the slave. Pass %NULL to break the - * bonding. The caller must hold the RTNL semaphore. On a failure - * a negative errno code is returned. On success %RTM_NEWLINK is sent - * to the routing socket and the function returns zero. + * netdev_master_upper_dev_get_rcu - Get master upper device + * @dev: device + * + * Find a master upper device and return pointer to it or NULL in case + * it's not there. The caller must hold the RCU read lock. */ -int netdev_set_bond_master(struct net_device *slave, struct net_device *master) +struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev) { - int err; + struct netdev_upper *upper; + + upper = list_first_or_null_rcu(&dev->upper_dev_list, + struct netdev_upper, list); + if (upper && likely(upper->master)) + return upper->dev; + return NULL; +} +EXPORT_SYMBOL(netdev_master_upper_dev_get_rcu); + +static int __netdev_upper_dev_link(struct net_device *dev, + struct net_device *upper_dev, bool master) +{ + struct netdev_upper *upper; ASSERT_RTNL(); - err = netdev_set_master(slave, master); - if (err) - return err; + if (dev == upper_dev) + return -EBUSY; + + /* To prevent loops, check if dev is not upper device to upper_dev. */ + if (__netdev_search_upper_dev(upper_dev, dev)) + return -EBUSY; + + if (__netdev_find_upper(dev, upper_dev)) + return -EEXIST; + + if (master && netdev_master_upper_dev_get(dev)) + return -EBUSY; + + upper = kmalloc(sizeof(*upper), GFP_KERNEL); + if (!upper) + return -ENOMEM; + + upper->dev = upper_dev; + upper->master = master; + INIT_LIST_HEAD(&upper->search_list); + + /* Ensure that master upper link is always the first item in list. */ if (master) - slave->flags |= IFF_SLAVE; + list_add_rcu(&upper->list, &dev->upper_dev_list); else - slave->flags &= ~IFF_SLAVE; + list_add_tail_rcu(&upper->list, &dev->upper_dev_list); + dev_hold(upper_dev); - rtmsg_ifinfo(RTM_NEWLINK, slave, IFF_SLAVE); return 0; } -EXPORT_SYMBOL(netdev_set_bond_master); + +/** + * netdev_upper_dev_link - Add a link to the upper device + * @dev: device + * @upper_dev: new upper device + * + * Adds a link to device which is upper to this one. The caller must hold + * the RTNL lock. On a failure a negative errno code is returned. + * On success the reference counts are adjusted and the function + * returns zero. + */ +int netdev_upper_dev_link(struct net_device *dev, + struct net_device *upper_dev) +{ + return __netdev_upper_dev_link(dev, upper_dev, false); +} +EXPORT_SYMBOL(netdev_upper_dev_link); + +/** + * netdev_master_upper_dev_link - Add a master link to the upper device + * @dev: device + * @upper_dev: new upper device + * + * Adds a link to device which is upper to this one. In this case, only + * one master upper device can be linked, although other non-master devices + * might be linked as well. The caller must hold the RTNL lock. + * On a failure a negative errno code is returned. On success the reference + * counts are adjusted and the function returns zero. + */ +int netdev_master_upper_dev_link(struct net_device *dev, + struct net_device *upper_dev) +{ + return __netdev_upper_dev_link(dev, upper_dev, true); +} +EXPORT_SYMBOL(netdev_master_upper_dev_link); + +/** + * netdev_upper_dev_unlink - Removes a link to upper device + * @dev: device + * @upper_dev: new upper device + * + * Removes a link to device which is upper to this one. The caller must hold + * the RTNL lock. + */ +void netdev_upper_dev_unlink(struct net_device *dev, + struct net_device *upper_dev) +{ + struct netdev_upper *upper; + + ASSERT_RTNL(); + + upper = __netdev_find_upper(dev, upper_dev); + if (!upper) + return; + list_del_rcu(&upper->list); + dev_put(upper_dev); + kfree_rcu(upper, rcu); +} +EXPORT_SYMBOL(netdev_upper_dev_unlink); static void dev_change_rx_flags(struct net_device *dev, int flags) { @@ -5503,8 +5670,8 @@ static void rollback_registered_many(struct list_head *head) if (dev->netdev_ops->ndo_uninit) dev->netdev_ops->ndo_uninit(dev); - /* Notifier chain MUST detach us from master device. */ - WARN_ON(dev->master); + /* Notifier chain MUST detach us all upper devices. */ + WARN_ON(netdev_has_any_upper_dev(dev)); /* Remove entries from kobject tree */ netdev_unregister_kobject(dev); @@ -6212,6 +6379,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, INIT_LIST_HEAD(&dev->napi_list); INIT_LIST_HEAD(&dev->unreg_list); INIT_LIST_HEAD(&dev->link_watch_list); + INIT_LIST_HEAD(&dev->upper_dev_list); dev->priv_flags = IFF_XMIT_DST_RELEASE; setup(dev); diff --git a/net/core/netpoll.c b/net/core/netpoll.c index 3151acf5ec13..d2bda8eb08ec 100644 --- a/net/core/netpoll.c +++ b/net/core/netpoll.c @@ -210,9 +210,12 @@ static void netpoll_poll_dev(struct net_device *dev) if (dev->flags & IFF_SLAVE) { if (ni) { - struct net_device *bond_dev = dev->master; + struct net_device *bond_dev; struct sk_buff *skb; - struct netpoll_info *bond_ni = rcu_dereference_bh(bond_dev->npinfo); + struct netpoll_info *bond_ni; + + bond_dev = netdev_master_upper_dev_get_rcu(dev); + bond_ni = rcu_dereference_bh(bond_dev->npinfo); while ((skb = skb_dequeue(&ni->arp_tx))) { skb->dev = bond_dev; skb_queue_tail(&bond_ni->arp_tx, skb); @@ -815,7 +818,7 @@ int netpoll_setup(struct netpoll *np) return -ENODEV; } - if (ndev->master) { + if (netdev_master_upper_dev_get(ndev)) { np_err(np, "%s is a slave device, aborting\n", np->dev_name); err = -EBUSY; goto put; diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 78d05a9dcea6..9969afb82271 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c @@ -880,6 +880,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, const struct rtnl_link_stats64 *stats; struct nlattr *attr, *af_spec; struct rtnl_af_ops *af_ops; + struct net_device *upper_dev = netdev_master_upper_dev_get(dev); ASSERT_RTNL(); nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifm), flags); @@ -908,8 +909,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, #endif (dev->ifindex != dev->iflink && nla_put_u32(skb, IFLA_LINK, dev->iflink)) || - (dev->master && - nla_put_u32(skb, IFLA_MASTER, dev->master->ifindex)) || + (upper_dev && + nla_put_u32(skb, IFLA_MASTER, upper_dev->ifindex)) || nla_put_u8(skb, IFLA_CARRIER, netif_carrier_ok(dev)) || (dev->qdisc && nla_put_string(skb, IFLA_QDISC, dev->qdisc->ops->id)) || @@ -1273,16 +1274,16 @@ static int do_setvfinfo(struct net_device *dev, struct nlattr *attr) static int do_set_master(struct net_device *dev, int ifindex) { - struct net_device *master_dev; + struct net_device *upper_dev = netdev_master_upper_dev_get(dev); const struct net_device_ops *ops; int err; - if (dev->master) { - if (dev->master->ifindex == ifindex) + if (upper_dev) { + if (upper_dev->ifindex == ifindex) return 0; - ops = dev->master->netdev_ops; + ops = upper_dev->netdev_ops; if (ops->ndo_del_slave) { - err = ops->ndo_del_slave(dev->master, dev); + err = ops->ndo_del_slave(upper_dev, dev); if (err) return err; } else { @@ -1291,12 +1292,12 @@ static int do_set_master(struct net_device *dev, int ifindex) } if (ifindex) { - master_dev = __dev_get_by_index(dev_net(dev), ifindex); - if (!master_dev) + upper_dev = __dev_get_by_index(dev_net(dev), ifindex); + if (!upper_dev) return -EINVAL; - ops = master_dev->netdev_ops; + ops = upper_dev->netdev_ops; if (ops->ndo_add_slave) { - err = ops->ndo_add_slave(master_dev, dev); + err = ops->ndo_add_slave(upper_dev, dev); if (err) return err; } else { @@ -1986,6 +1987,7 @@ errout: if (err < 0) rtnl_set_sk_err(net, RTNLGRP_LINK, err); } +EXPORT_SYMBOL(rtmsg_ifinfo); static int nlmsg_populate_fdb_fill(struct sk_buff *skb, struct net_device *dev, @@ -2048,7 +2050,6 @@ errout: static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) { struct net *net = sock_net(skb->sk); - struct net_device *master = NULL; struct ndmsg *ndm; struct nlattr *tb[NDA_MAX+1]; struct net_device *dev; @@ -2090,10 +2091,10 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) /* Support fdb on master device the net/bridge default case */ if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) && (dev->priv_flags & IFF_BRIDGE_PORT)) { - master = dev->master; - err = master->netdev_ops->ndo_fdb_add(ndm, tb, - dev, addr, - nlh->nlmsg_flags); + struct net_device *br_dev = netdev_master_upper_dev_get(dev); + const struct net_device_ops *ops = br_dev->netdev_ops; + + err = ops->ndo_fdb_add(ndm, tb, dev, addr, nlh->nlmsg_flags); if (err) goto out; else @@ -2154,10 +2155,11 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) /* Support fdb on master device the net/bridge default case */ if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) && (dev->priv_flags & IFF_BRIDGE_PORT)) { - struct net_device *master = dev->master; + struct net_device *br_dev = netdev_master_upper_dev_get(dev); + const struct net_device_ops *ops = br_dev->netdev_ops; - if (master->netdev_ops->ndo_fdb_del) - err = master->netdev_ops->ndo_fdb_del(ndm, dev, addr); + if (ops->ndo_fdb_del) + err = ops->ndo_fdb_del(ndm, dev, addr); if (err) goto out; @@ -2241,9 +2243,11 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb) rcu_read_lock(); for_each_netdev_rcu(net, dev) { if (dev->priv_flags & IFF_BRIDGE_PORT) { - struct net_device *master = dev->master; - const struct net_device_ops *ops = master->netdev_ops; + struct net_device *br_dev; + const struct net_device_ops *ops; + br_dev = netdev_master_upper_dev_get(dev); + ops = br_dev->netdev_ops; if (ops->ndo_fdb_dump) idx = ops->ndo_fdb_dump(skb, cb, dev, idx); } @@ -2264,6 +2268,7 @@ int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, struct ifinfomsg *ifm; struct nlattr *br_afspec; u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN; + struct net_device *br_dev = netdev_master_upper_dev_get(dev); nlh = nlmsg_put(skb, pid, seq, RTM_NEWLINK, sizeof(*ifm), NLM_F_MULTI); if (nlh == NULL) @@ -2281,8 +2286,8 @@ int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, if (nla_put_string(skb, IFLA_IFNAME, dev->name) || nla_put_u32(skb, IFLA_MTU, dev->mtu) || nla_put_u8(skb, IFLA_OPERSTATE, operstate) || - (dev->master && - nla_put_u32(skb, IFLA_MASTER, dev->master->ifindex)) || + (br_dev && + nla_put_u32(skb, IFLA_MASTER, br_dev->ifindex)) || (dev->addr_len && nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) || (dev->ifindex != dev->iflink && @@ -2318,11 +2323,11 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb) rcu_read_lock(); for_each_netdev_rcu(net, dev) { const struct net_device_ops *ops = dev->netdev_ops; - struct net_device *master = dev->master; + struct net_device *br_dev = netdev_master_upper_dev_get(dev); - if (master && master->netdev_ops->ndo_bridge_getlink) { + if (br_dev && br_dev->netdev_ops->ndo_bridge_getlink) { if (idx >= cb->args[0] && - master->netdev_ops->ndo_bridge_getlink( + br_dev->netdev_ops->ndo_bridge_getlink( skb, portid, seq, dev) < 0) break; idx++; @@ -2359,7 +2364,7 @@ static inline size_t bridge_nlmsg_size(void) static int rtnl_bridge_notify(struct net_device *dev, u16 flags) { struct net *net = dev_net(dev); - struct net_device *master = dev->master; + struct net_device *br_dev = netdev_master_upper_dev_get(dev); struct sk_buff *skb; int err = -EOPNOTSUPP; @@ -2370,8 +2375,8 @@ static int rtnl_bridge_notify(struct net_device *dev, u16 flags) } if ((!flags || (flags & BRIDGE_FLAGS_MASTER)) && - master && master->netdev_ops->ndo_bridge_getlink) { - err = master->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev); + br_dev && br_dev->netdev_ops->ndo_bridge_getlink) { + err = br_dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev); if (err < 0) goto errout; } @@ -2430,13 +2435,14 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, oflags = flags; if (!flags || (flags & BRIDGE_FLAGS_MASTER)) { - if (!dev->master || - !dev->master->netdev_ops->ndo_bridge_setlink) { + struct net_device *br_dev = netdev_master_upper_dev_get(dev); + + if (!br_dev || !br_dev->netdev_ops->ndo_bridge_setlink) { err = -EOPNOTSUPP; goto out; } - err = dev->master->netdev_ops->ndo_bridge_setlink(dev, nlh); + err = br_dev->netdev_ops->ndo_bridge_setlink(dev, nlh); if (err) goto out; |