Commit 912df262 authored by David S. Miller's avatar David S. Miller

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: default avatarDavid S. Miller <davem@davemloft.net>
parents 04e406dc 85464ef2
...@@ -135,6 +135,7 @@ static int nes_inetaddr_event(struct notifier_block *notifier, ...@@ -135,6 +135,7 @@ static int nes_inetaddr_event(struct notifier_block *notifier,
struct net_device *event_netdev = ifa->ifa_dev->dev; struct net_device *event_netdev = ifa->ifa_dev->dev;
struct nes_device *nesdev; struct nes_device *nesdev;
struct net_device *netdev; struct net_device *netdev;
struct net_device *upper_dev;
struct nes_vnic *nesvnic; struct nes_vnic *nesvnic;
unsigned int is_bonded; unsigned int is_bonded;
...@@ -145,8 +146,9 @@ static int nes_inetaddr_event(struct notifier_block *notifier, ...@@ -145,8 +146,9 @@ static int nes_inetaddr_event(struct notifier_block *notifier,
nesdev, nesdev->netdev[0]->name); nesdev, nesdev->netdev[0]->name);
netdev = nesdev->netdev[0]; netdev = nesdev->netdev[0];
nesvnic = netdev_priv(netdev); nesvnic = netdev_priv(netdev);
upper_dev = netdev_master_upper_dev_get(netdev);
is_bonded = netif_is_bond_slave(netdev) && is_bonded = netif_is_bond_slave(netdev) &&
(netdev->master == event_netdev); (upper_dev == event_netdev);
if ((netdev == event_netdev) || is_bonded) { if ((netdev == event_netdev) || is_bonded) {
if (nesvnic->rdma_enabled == 0) { if (nesvnic->rdma_enabled == 0) {
nes_debug(NES_DBG_NETDEV, "Returning without processing event for %s since" 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, ...@@ -179,9 +181,9 @@ static int nes_inetaddr_event(struct notifier_block *notifier,
/* fall through */ /* fall through */
case NETDEV_CHANGEADDR: case NETDEV_CHANGEADDR:
/* Add the address to the IP table */ /* Add the address to the IP table */
if (netdev->master) if (upper_dev)
nesvnic->local_ipaddr = 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 else
nesvnic->local_ipaddr = ifa->ifa_address; nesvnic->local_ipaddr = ifa->ifa_address;
......
...@@ -1340,7 +1340,7 @@ static int nes_addr_resolve_neigh(struct nes_vnic *nesvnic, u32 dst_ip, int arpi ...@@ -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)) if (netif_is_bond_slave(nesvnic->netdev))
netdev = nesvnic->netdev->master; netdev = netdev_master_upper_dev_get(nesvnic->netdev);
else else
netdev = nesvnic->netdev; netdev = nesvnic->netdev;
......
...@@ -1127,7 +1127,7 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port) ...@@ -1127,7 +1127,7 @@ static void ad_rx_machine(struct lacpdu *lacpdu, struct port *port)
// INFO_RECEIVED_LOOPBACK_FRAMES // INFO_RECEIVED_LOOPBACK_FRAMES
pr_err("%s: An illegal loopback occurred on adapter (%s).\n" 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", "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; return;
} }
__update_selected(lacpdu, port); __update_selected(lacpdu, port);
...@@ -1306,7 +1306,7 @@ static void ad_port_selection_logic(struct port *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 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", 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->actor_port_number,
port->slave->dev->name, port->slave->dev->name,
port->aggregator->aggregator_identifier); port->aggregator->aggregator_identifier);
...@@ -1386,7 +1386,7 @@ static void ad_port_selection_logic(struct port *port) ...@@ -1386,7 +1386,7 @@ static void ad_port_selection_logic(struct port *port)
port->aggregator->aggregator_identifier); port->aggregator->aggregator_identifier);
} else { } else {
pr_err("%s: Port %d (on %s) did not find a suitable aggregator\n", 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); port->actor_port_number, port->slave->dev->name);
} }
} }
...@@ -1463,7 +1463,7 @@ static struct aggregator *ad_agg_selection_test(struct aggregator *best, ...@@ -1463,7 +1463,7 @@ static struct aggregator *ad_agg_selection_test(struct aggregator *best,
default: default:
pr_warning("%s: Impossible agg select mode %d\n", 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)); __get_agg_selection_mode(curr->lag_ports));
break; break;
} }
...@@ -1571,7 +1571,7 @@ static void ad_agg_selection_logic(struct aggregator *agg) ...@@ -1571,7 +1571,7 @@ static void ad_agg_selection_logic(struct aggregator *agg)
// check if any partner replys // check if any partner replys
if (best->is_individual) { if (best->is_individual) {
pr_warning("%s: Warning: No 802.3ad response from the link partner for any adapters in the bond\n", 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; best->is_active = 1;
...@@ -1898,7 +1898,7 @@ int bond_3ad_bind_slave(struct slave *slave) ...@@ -1898,7 +1898,7 @@ int bond_3ad_bind_slave(struct slave *slave)
if (bond == NULL) { if (bond == NULL) {
pr_err("%s: The slave %s is not attached to its bond\n", 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; return -1;
} }
...@@ -1973,7 +1973,7 @@ void bond_3ad_unbind_slave(struct slave *slave) ...@@ -1973,7 +1973,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
// if slave is null, the whole port is not initialized // if slave is null, the whole port is not initialized
if (!port->slave) { if (!port->slave) {
pr_warning("Warning: %s: Trying to unbind an uninitialized port on %s\n", 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; return;
} }
...@@ -2009,7 +2009,7 @@ void bond_3ad_unbind_slave(struct slave *slave) ...@@ -2009,7 +2009,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
if ((new_aggregator->lag_ports == port) && new_aggregator->is_active) { if ((new_aggregator->lag_ports == port) && new_aggregator->is_active) {
pr_info("%s: Removing an active aggregator\n", 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 aggregator
select_new_active_agg = 1; select_new_active_agg = 1;
} }
...@@ -2040,7 +2040,7 @@ void bond_3ad_unbind_slave(struct slave *slave) ...@@ -2040,7 +2040,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
ad_agg_selection_logic(__get_first_agg(port)); ad_agg_selection_logic(__get_first_agg(port));
} else { } else {
pr_warning("%s: Warning: unbinding aggregator, and could not find a new aggregator for its ports\n", 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 } 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; select_new_active_agg = aggregator->is_active;
...@@ -2048,7 +2048,7 @@ void bond_3ad_unbind_slave(struct slave *slave) ...@@ -2048,7 +2048,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
ad_clear_agg(aggregator); ad_clear_agg(aggregator);
if (select_new_active_agg) { if (select_new_active_agg) {
pr_info("%s: Removing an active aggregator\n", pr_info("%s: Removing an active aggregator\n",
slave->dev->master->name); slave->bond->dev->name);
// select new active aggregator // select new active aggregator
ad_agg_selection_logic(__get_first_agg(port)); ad_agg_selection_logic(__get_first_agg(port));
} }
...@@ -2076,7 +2076,7 @@ void bond_3ad_unbind_slave(struct slave *slave) ...@@ -2076,7 +2076,7 @@ void bond_3ad_unbind_slave(struct slave *slave)
ad_clear_agg(temp_aggregator); ad_clear_agg(temp_aggregator);
if (select_new_active_agg) { if (select_new_active_agg) {
pr_info("%s: Removing an active aggregator\n", pr_info("%s: Removing an active aggregator\n",
slave->dev->master->name); slave->bond->dev->name);
// select new active aggregator // select new active aggregator
ad_agg_selection_logic(__get_first_agg(port)); 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 ...@@ -2184,7 +2184,7 @@ static int bond_3ad_rx_indication(struct lacpdu *lacpdu, struct slave *slave, u1
if (!port->slave) { if (!port->slave) {
pr_warning("%s: Warning: port of slave %s is uninitialized\n", 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; return ret;
} }
...@@ -2240,7 +2240,7 @@ void bond_3ad_adapter_speed_changed(struct slave *slave) ...@@ -2240,7 +2240,7 @@ void bond_3ad_adapter_speed_changed(struct slave *slave)
// if slave is null, the whole port is not initialized // if slave is null, the whole port is not initialized
if (!port->slave) { if (!port->slave) {
pr_warning("Warning: %s: speed changed for uninitialized port on %s\n", 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; return;
} }
...@@ -2268,7 +2268,7 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave) ...@@ -2268,7 +2268,7 @@ void bond_3ad_adapter_duplex_changed(struct slave *slave)
// if slave is null, the whole port is not initialized // if slave is null, the whole port is not initialized
if (!port->slave) { if (!port->slave) {
pr_warning("%s: Warning: duplex changed for uninitialized port on %s\n", 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; return;
} }
...@@ -2297,7 +2297,7 @@ void bond_3ad_handle_link_change(struct slave *slave, char link) ...@@ -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 slave is null, the whole port is not initialized
if (!port->slave) { if (!port->slave) {
pr_warning("Warning: %s: link status changed for uninitialized port on %s\n", 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; return;
} }
......
...@@ -507,7 +507,7 @@ static void rlb_update_client(struct rlb_client_info *client_info) ...@@ -507,7 +507,7 @@ static void rlb_update_client(struct rlb_client_info *client_info)
client_info->mac_dst); client_info->mac_dst);
if (!skb) { if (!skb) {
pr_err("%s: Error: failed to create an ARP packet\n", pr_err("%s: Error: failed to create an ARP packet\n",
client_info->slave->dev->master->name); client_info->slave->bond->dev->name);
continue; continue;
} }
...@@ -517,7 +517,7 @@ static void rlb_update_client(struct rlb_client_info *client_info) ...@@ -517,7 +517,7 @@ static void rlb_update_client(struct rlb_client_info *client_info)
skb = vlan_put_tag(skb, client_info->vlan_id); skb = vlan_put_tag(skb, client_info->vlan_id);
if (!skb) { if (!skb) {
pr_err("%s: Error: failed to insert VLAN tag\n", pr_err("%s: Error: failed to insert VLAN tag\n",
client_info->slave->dev->master->name); client_info->slave->bond->dev->name);
continue; continue;
} }
} }
...@@ -1043,7 +1043,7 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[]) ...@@ -1043,7 +1043,7 @@ static int alb_set_slave_mac_addr(struct slave *slave, u8 addr[])
if (dev_set_mac_address(dev, &s_addr)) { if (dev_set_mac_address(dev, &s_addr)) {
pr_err("%s: Error: dev_set_mac_address of dev %s failed!\n" 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", "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 -EOPNOTSUPP;
} }
return 0; return 0;
......
...@@ -746,11 +746,9 @@ static void __bond_resend_igmp_join_requests(struct net_device *dev) ...@@ -746,11 +746,9 @@ static void __bond_resend_igmp_join_requests(struct net_device *dev)
{ {
struct in_device *in_dev; struct in_device *in_dev;
rcu_read_lock();
in_dev = __in_dev_get_rcu(dev); in_dev = __in_dev_get_rcu(dev);
if (in_dev) if (in_dev)
ip_mc_rejoin_groups(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) ...@@ -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) 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; struct vlan_entry *vlan;
rcu_read_lock();
read_lock(&bond->lock); read_lock(&bond->lock);
bond_dev = bond->dev; bond_dev = bond->dev;
...@@ -774,18 +773,14 @@ static void bond_resend_igmp_join_requests(struct bonding *bond) ...@@ -774,18 +773,14 @@ static void bond_resend_igmp_join_requests(struct bonding *bond)
* if bond is enslaved to a bridge, * if bond is enslaved to a bridge,
* then rejoin all groups on its master * then rejoin all groups on its master
*/ */
master_dev = bond_dev->master; upper_dev = netdev_master_upper_dev_get_rcu(bond_dev);
if (master_dev) if (upper_dev && upper_dev->priv_flags & IFF_EBRIDGE)
if ((master_dev->priv_flags & IFF_EBRIDGE) __bond_resend_igmp_join_requests(upper_dev);
&& (bond_dev->priv_flags & IFF_BRIDGE_PORT))
__bond_resend_igmp_join_requests(master_dev);
/* rejoin all groups on vlan devices */ /* rejoin all groups on vlan devices */
list_for_each_entry(vlan, &bond->vlan_list, vlan_list) { list_for_each_entry(vlan, &bond->vlan_list, vlan_list) {
rcu_read_lock();
vlan_dev = __vlan_find_dev_deep(bond_dev, vlan_dev = __vlan_find_dev_deep(bond_dev,
vlan->vlan_id); vlan->vlan_id);
rcu_read_unlock();
if (vlan_dev) if (vlan_dev)
__bond_resend_igmp_join_requests(vlan_dev); __bond_resend_igmp_join_requests(vlan_dev);
} }
...@@ -794,13 +789,16 @@ static void bond_resend_igmp_join_requests(struct bonding *bond) ...@@ -794,13 +789,16 @@ static void bond_resend_igmp_join_requests(struct bonding *bond)
queue_delayed_work(bond->wq, &bond->mcast_work, HZ/5); queue_delayed_work(bond->wq, &bond->mcast_work, HZ/5);
read_unlock(&bond->lock); read_unlock(&bond->lock);
rcu_read_unlock();
} }
static void bond_resend_igmp_join_requests_delayed(struct work_struct *work) static void bond_resend_igmp_join_requests_delayed(struct work_struct *work)
{ {
struct bonding *bond = container_of(work, struct bonding, struct bonding *bond = container_of(work, struct bonding,
mcast_work.work); mcast_work.work);
rcu_read_lock();
bond_resend_igmp_join_requests(bond); 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) ...@@ -1493,6 +1491,27 @@ static rx_handler_result_t bond_handle_frame(struct sk_buff **pskb)
return ret; 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> */ /* enslave device <slave> to bond device <master> */
int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) 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) ...@@ -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) { 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; goto err_restore_mac;
} }
...@@ -1891,7 +1910,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev) ...@@ -1891,7 +1910,7 @@ int bond_enslave(struct net_device *bond_dev, struct net_device *slave_dev)
dev_close(slave_dev); dev_close(slave_dev);
err_unset_master: err_unset_master:
netdev_set_bond_master(slave_dev, NULL); bond_upper_dev_unlink(bond_dev, slave_dev);
err_restore_mac: err_restore_mac:
if (!bond->params.fail_over_mac) { if (!bond->params.fail_over_mac) {
...@@ -1936,7 +1955,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev) ...@@ -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 */ /* slave is not a slave or master is not master of this slave */
if (!(slave_dev->flags & IFF_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", pr_err("%s: Error: cannot release %s.\n",
bond_dev->name, slave_dev->name); bond_dev->name, slave_dev->name);
return -EINVAL; return -EINVAL;
...@@ -2080,7 +2099,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev) ...@@ -2080,7 +2099,7 @@ int bond_release(struct net_device *bond_dev, struct net_device *slave_dev)
netif_addr_unlock_bh(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); slave_disable_netpoll(slave);
...@@ -2195,7 +2214,7 @@ static int bond_release_all(struct net_device *bond_dev) ...@@ -2195,7 +2214,7 @@ static int bond_release_all(struct net_device *bond_dev)
netif_addr_unlock_bh(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); slave_disable_netpoll(slave);
...@@ -2259,8 +2278,9 @@ static int bond_ioctl_change_active(struct net_device *bond_dev, struct net_devi ...@@ -2259,8 +2278,9 @@ static int bond_ioctl_change_active(struct net_device *bond_dev, struct net_devi
if (!USES_PRIMARY(bond->params.mode)) if (!USES_PRIMARY(bond->params.mode))
return -EINVAL; return -EINVAL;
/* Verify that master_dev is indeed the master of slave_dev */ /* Verify that bond_dev is indeed the master of slave_dev */
if (!(slave_dev->flags & IFF_SLAVE) || (slave_dev->master != bond_dev)) if (!(slave_dev->flags & IFF_SLAVE) ||
!netdev_has_upper_dev(slave_dev, bond_dev))
return -EINVAL; return -EINVAL;
read_lock(&bond->lock); read_lock(&bond->lock);
...@@ -3258,36 +3278,32 @@ static int bond_master_netdev_event(unsigned long event, ...@@ -3258,36 +3278,32 @@ static int bond_master_netdev_event(unsigned long event,
static int bond_slave_netdev_event(unsigned long event, static int bond_slave_netdev_event(unsigned long event,
struct net_device *slave_dev) struct net_device *slave_dev)
{ {
struct net_device *bond_dev = slave_dev->master; struct slave *slave = bond_slave_get_rtnl(slave_dev);
struct bonding *bond = netdev_priv(bond_dev); struct bonding *bond = slave->bond;
struct slave *slave = NULL; struct net_device *bond_dev = slave->bond->dev;
u32 old_speed;
u8 old_duplex;
switch (event) { switch (event) {
case NETDEV_UNREGISTER: case NETDEV_UNREGISTER:
if (bond_dev) { if (bond->setup_by_slave)
if (bond->setup_by_slave) bond_release_and_destroy(bond_dev, slave_dev);
bond_release_and_destroy(bond_dev, slave_dev); else
else bond_release(bond_dev, slave_dev);
bond_release(bond_dev, slave_dev);
}
break; break;
case NETDEV_UP: case NETDEV_UP:
case NETDEV_CHANGE: case NETDEV_CHANGE:
slave = bond_get_slave_by_dev(bond, slave_dev); old_speed = slave->speed;
if (slave) { old_duplex = slave->duplex;
u32 old_speed = slave->speed;
u8 old_duplex = slave->duplex;
bond_update_speed_duplex(slave); bond_update_speed_duplex(slave);
if (bond->params.mode == BOND_MODE_8023AD) { if (bond->params.mode == BOND_MODE_8023AD) {
if (old_speed != slave->speed) if (old_speed != slave->speed)
bond_3ad_adapter_speed_changed(slave); bond_3ad_adapter_speed_changed(slave);
if (old_duplex != slave->duplex) if (old_duplex != slave->duplex)
bond_3ad_adapter_duplex_changed(slave); bond_3ad_adapter_duplex_changed(slave);
}
} }
break; break;
case NETDEV_DOWN: case NETDEV_DOWN:
/* /*
......
...@@ -258,6 +258,9 @@ static inline bool bond_vlan_used(struct bonding *bond) ...@@ -258,6 +258,9 @@ static inline bool bond_vlan_used(struct bonding *bond)
#define bond_slave_get_rcu(dev) \ #define bond_slave_get_rcu(dev) \
((struct slave *) rcu_dereference(dev->rx_handler_data)) ((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 * 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, ...@@ -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) static inline struct bonding *bond_get_bond_by_slave(struct slave *slave)
{ {
if (!slave || !slave->dev->master) { if (!slave || !slave->bond)
return NULL; return NULL;
} return slave->bond;
return netdev_priv(slave->dev->master);
} }
static inline bool bond_is_lb(const struct bonding *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, ...@@ -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) static inline void bond_set_slave_inactive_flags(struct slave *slave)
{ {
struct bonding *bond = netdev_priv(slave->dev->master); if (!bond_is_lb(slave->bond))
if (!bond_is_lb(bond))
bond_set_backup_slave(slave); bond_set_backup_slave(slave);
if (!bond->params.all_slaves_active) if (!slave->bond->params.all_slaves_active)
slave->inactive = 1; slave->inactive = 1;
} }
......
...@@ -182,14 +182,17 @@ static struct net_device *get_iff_from_mac(struct adapter *adapter, ...@@ -182,14 +182,17 @@ static struct net_device *get_iff_from_mac(struct adapter *adapter,
struct net_device *dev = adapter->port[i]; struct net_device *dev = adapter->port[i];
if (!memcmp(dev->dev_addr, mac, ETH_ALEN)) { if (!memcmp(dev->dev_addr, mac, ETH_ALEN)) {
rcu_read_lock();
if (vlan && vlan != VLAN_VID_MASK) { if (vlan && vlan != VLAN_VID_MASK) {
rcu_read_lock();
dev = __vlan_find_dev_deep(dev, vlan); dev = __vlan_find_dev_deep(dev, vlan);
rcu_read_unlock();
} else if (netif_is_bond_slave(dev)) { } else if (netif_is_bond_slave(dev)) {
while (dev->master) struct net_device *upper_dev;
dev = dev->master;
while ((upper_dev =
netdev_master_upper_dev_get_rcu(dev)))
dev = upper_dev;
} }
rcu_read_unlock();
return dev; return dev;
} }
} }
......
...@@ -3186,12 +3186,14 @@ void qlcnic_restore_indev_addr(struct net_device *netdev, unsigned long event) ...@@ -3186,12 +3186,14 @@ void qlcnic_restore_indev_addr(struct net_device *netdev, unsigned long event)
qlcnic_config_indev_addr(adapter, netdev, event); qlcnic_config_indev_addr(adapter, netdev, event);
rcu_read_lock();
for_each_set_bit(vid, adapter->vlans, VLAN_N_VID) { for_each_set_bit(vid, adapter->vlans, VLAN_N_VID) {
dev = __vlan_find_dev_deep(netdev, vid); dev = __vlan_find_dev_deep(netdev, vid);
if (!dev) if (!dev)
continue; continue;
qlcnic_config_indev_addr(adapter, dev, event); qlcnic_config_indev_addr(adapter, dev, event);
} }
rcu_read_unlock();
} }
static int qlcnic_netdev_event(struct notifier_block *this, static int qlcnic_netdev_event(struct notifier_block *this,
......
...@@ -764,16 +764,22 @@ int macvlan_common_newlink(struct net *src_net, struct net_device *dev, ...@@ -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); memcpy(dev->dev_addr, lowerdev->dev_addr, ETH_ALEN);
} }
err = netdev_upper_dev_link(lowerdev, dev);
if (err)
goto destroy_port;
port->count += 1; port->count += 1;
err = register_netdevice(dev); err = register_netdevice(dev);
if (err < 0) if (err < 0)
goto destroy_port; goto upper_dev_unlink;
list_add_tail(&vlan->list, &port->vlans); list_add_tail(&vlan->list, &port->vlans);
netif_stacked_transfer_operstate(lowerdev, dev); netif_stacked_transfer_operstate(lowerdev, dev);
return 0; return 0;
upper_dev_unlink:
netdev_upper_dev_unlink(lowerdev, dev);
destroy_port: destroy_port:
port->count -= 1; port->count -= 1;
if (!port->count) if (!port->count)
...@@ -797,6 +803,7 @@ void macvlan_dellink(struct net_device *dev, struct list_head *head) ...@@ -797,6 +803,7 @@ void macvlan_dellink(struct net_device *dev, struct list_head *head)
list_del(&vlan->list); list_del(&vlan->list);
unregister_netdevice_queue(dev, head); unregister_netdevice_queue(dev, head);
netdev_upper_dev_unlink(vlan->lowerdev, dev);
} }
EXPORT_SYMBOL_GPL(macvlan_dellink); EXPORT_SYMBOL_GPL(macvlan_dellink);
......
...@@ -1055,10 +1055,11 @@ static int team_port_add(struct team *team, struct net_device *port_dev) ...@@ -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) { if (err) {
netdev_err(dev, "Device %s failed to set master\n", portname); netdev_err(dev, "Device %s failed to set upper link\n",
goto err_set_master; portname);
goto err_set_upper_link;
} }
err = netdev_rx_handler_register(port_dev, team_handle_frame, err = netdev_rx_handler_register(port_dev, team_handle_frame,
...@@ -1091,9 +1092,9 @@ static int team_port_add(struct team *team, struct net_device *port_dev) ...@@ -1091,9 +1092,9 @@ static int team_port_add(struct team *team, struct net_device *port_dev)
netdev_rx_handler_unregister(port_dev); netdev_rx_handler_unregister(port_dev);
err_handler_register: 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); team_port_disable_netpoll(port);
err_enable_netpoll: err_enable_netpoll:
...@@ -1137,7 +1138,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev) ...@@ -1137,7 +1138,7 @@ static int team_port_del(struct team *team, struct net_device *port_dev)
team_port_disable(team, port); team_port_disable(team, port);
list_del_rcu(&port->list); list_del_rcu(&port->list);
netdev_rx_handler_unregister(port_dev); netdev_rx_handler_unregister(port_dev);
netdev_set_master(port_dev, NULL); netdev_upper_dev_unlink(port_dev, dev);
team_port_disable_netpoll(port); team_port_disable_netpoll(port);
vlan_vids_del_by_dev(port_dev, dev); vlan_vids_del_by_dev(port_dev, dev);
dev_close(port_dev); dev_close(port_dev);
......
...@@ -1640,6 +1640,7 @@ static void qeth_l3_add_mc(struct qeth_card *card, struct in_device *in4_dev) ...@@ -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) static void qeth_l3_add_vlan_mc(struct qeth_card *card)
{ {
struct in_device *in_dev; struct in_device *in_dev;
...@@ -1652,19 +1653,14 @@ static void qeth_l3_add_vlan_mc(struct qeth_card *card) ...@@ -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) { for_each_set_bit(vid, card->active_vlans, VLAN_N_VID) {
struct net_device *netdev; struct net_device *netdev;
rcu_read_lock();
netdev = __vlan_find_dev_deep(card->dev, vid); netdev = __vlan_find_dev_deep(card->dev, vid);
rcu_read_unlock();
if (netdev == NULL || if (netdev == NULL ||
!(netdev->flags & IFF_UP)) !(netdev->flags & IFF_UP))
continue; continue;
in_dev = in_dev_get(netdev); in_dev = __in_dev_get_rcu(netdev);
if (!in_dev) if (!in_dev)
continue; continue;
rcu_read_lock();
qeth_l3_add_mc(card, in_dev); 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) ...@@ -1673,14 +1669,14 @@ static void qeth_l3_add_multicast_ipv4(struct qeth_card *card)
struct in_device *in4_dev; struct in_device *in4_dev;
QETH_CARD_TEXT(card, 4, "chkmcv4"); QETH_CARD_TEXT(card, 4, "chkmcv4");
in4_dev = in_dev_get(card->dev);
if (in4_dev == NULL)
return;
rcu_read_lock(); 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_mc(card, in4_dev);
qeth_l3_add_vlan_mc(card); qeth_l3_add_vlan_mc(card);
unlock:
rcu_read_unlock(); rcu_read_unlock();
in_dev_put(in4_dev);
} }
#ifdef CONFIG_QETH_IPV6 #ifdef CONFIG_QETH_IPV6
...@@ -1705,6 +1701,7 @@ static void qeth_l3_add_mc6(struct qeth_card *card, struct inet6_dev *in6_dev) ...@@ -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) static void qeth_l3_add_vlan_mc6(struct qeth_card *card)
{ {
struct inet6_dev *in_dev; struct inet6_dev *in_dev;
...@@ -1741,10 +1738,12 @@ static void qeth_l3_add_multicast_ipv6(struct qeth_card *card) ...@@ -1741,10 +1738,12 @@ static void qeth_l3_add_multicast_ipv6(struct qeth_card *card)
in6_dev = in6_dev_get(card->dev); in6_dev = in6_dev_get(card->dev);
if (in6_dev == NULL) if (in6_dev == NULL)
return; return;
rcu_read_lock();
read_lock_bh(&in6_dev->lock); read_lock_bh(&in6_dev->lock);
qeth_l3_add_mc6(card, in6_dev); qeth_l3_add_mc6(card, in6_dev);
qeth_l3_add_vlan_mc6(card); qeth_l3_add_vlan_mc6(card);
read_unlock_bh(&in6_dev->lock); read_unlock_bh(&in6_dev->lock);
rcu_read_unlock();
in6_dev_put(in6_dev); in6_dev_put(in6_dev);
} }
#endif /* CONFIG_QETH_IPV6 */ #endif /* CONFIG_QETH_IPV6 */
...@@ -1813,8 +1812,10 @@ static void qeth_l3_free_vlan_addresses6(struct qeth_card *card, ...@@ -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, static void qeth_l3_free_vlan_addresses(struct qeth_card *card,
unsigned short vid) unsigned short vid)
{ {
rcu_read_lock();
qeth_l3_free_vlan_addresses4(card, vid); qeth_l3_free_vlan_addresses4(card, vid);
qeth_l3_free_vlan_addresses6(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) static int qeth_l3_vlan_rx_add_vid(struct net_device *dev, unsigned short vid)
......
...@@ -858,8 +858,7 @@ struct netdev_fcoe_hbainfo { ...@@ -858,8 +858,7 @@ struct netdev_fcoe_hbainfo {
* flow_id is a flow ID to be passed to rps_may_expire_flow() later. * 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. * Return the filter ID on success, or a negative error code.
* *
* Slave management functions (for bridge, bonding, etc). User should * Slave management functions (for bridge, bonding, etc).
* call netdev_set_master() to set dev->master properly.
* int (*ndo_add_slave)(struct net_device *dev, struct net_device *slave_dev); * int (*ndo_add_slave)(struct net_device *dev, struct net_device *slave_dev);
* Called to make another netdev an underling. * Called to make another netdev an underling.
* *
...@@ -1170,9 +1169,7 @@ struct net_device { ...@@ -1170,9 +1169,7 @@ struct net_device {
* avoid dirtying this cache line. * avoid dirtying this cache line.
*/ */
struct net_device *master; /* Pointer to master device of a group, struct list_head upper_dev_list; /* List of upper devices */
* which this device is member of.
*/
/* Interface address info used in eth_type_trans() */ /* Interface address info used in eth_type_trans() */
unsigned char *dev_addr; /* hw address, (before bcast unsigned char *dev_addr; /* hw address, (before bcast
...@@ -2636,9 +2633,18 @@ extern int netdev_max_backlog; ...@@ -2636,9 +2633,18 @@ extern int netdev_max_backlog;
extern int netdev_tstamp_prequeue; extern int netdev_tstamp_prequeue;
extern int weight_p; extern int weight_p;
extern int bpf_jit_enable; 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, extern bool netdev_has_upper_dev(struct net_device *dev,
struct net_device *master); 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 int skb_checksum_help(struct sk_buff *skb);
extern struct sk_buff *skb_gso_segment(struct sk_buff *skb, extern struct sk_buff *skb_gso_segment(struct sk_buff *skb,
netdev_features_t features); netdev_features_t features);
......
...@@ -105,6 +105,8 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head) ...@@ -105,6 +105,8 @@ void unregister_vlan_dev(struct net_device *dev, struct list_head *head)
*/ */
unregister_netdevice_queue(dev, head); unregister_netdevice_queue(dev, head);
netdev_upper_dev_unlink(real_dev, dev);
if (grp->nr_vlan_devs == 0) if (grp->nr_vlan_devs == 0)
vlan_gvrp_uninit_applicant(real_dev); vlan_gvrp_uninit_applicant(real_dev);
...@@ -162,9 +164,13 @@ int register_vlan_dev(struct net_device *dev) ...@@ -162,9 +164,13 @@ int register_vlan_dev(struct net_device *dev)
if (err < 0) if (err < 0)
goto out_uninit_applicant; goto out_uninit_applicant;
err = netdev_upper_dev_link(real_dev, dev);
if (err)
goto out_uninit_applicant;
err = register_netdevice(dev); err = register_netdevice(dev);
if (err < 0) if (err < 0)
goto out_uninit_applicant; goto out_upper_dev_unlink;
/* Account for reference in struct vlan_dev_priv */ /* Account for reference in struct vlan_dev_priv */
dev_hold(real_dev); dev_hold(real_dev);
...@@ -180,6 +186,8 @@ int register_vlan_dev(struct net_device *dev) ...@@ -180,6 +186,8 @@ int register_vlan_dev(struct net_device *dev)
return 0; return 0;
out_upper_dev_unlink:
netdev_upper_dev_unlink(real_dev, dev);
out_uninit_applicant: out_uninit_applicant:
if (grp->nr_vlan_devs == 0) if (grp->nr_vlan_devs == 0)
vlan_gvrp_uninit_applicant(real_dev); vlan_gvrp_uninit_applicant(real_dev);
......
...@@ -60,21 +60,25 @@ bool vlan_do_receive(struct sk_buff **skbp) ...@@ -60,21 +60,25 @@ bool vlan_do_receive(struct sk_buff **skbp)
return true; return true;
} }
/* Must be invoked with rcu_read_lock or with RTNL. */ /* Must be invoked with rcu_read_lock. */
struct net_device *__vlan_find_dev_deep(struct net_device *real_dev, struct net_device *__vlan_find_dev_deep(struct net_device *dev,
u16 vlan_id) 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) { if (vlan_info) {
return vlan_group_get_device(&vlan_info->grp, vlan_id); return vlan_group_get_device(&vlan_info->grp, vlan_id);
} else { } else {
/* /*
* Bonding slaves do not have grp assigned to themselves. * Lower devices of master uppers (bonding, team) do not have
* Grp is assigned to bonding master instead. * grp assigned to themselves. Grp is assigned to upper device
* instead.
*/ */
if (netif_is_bond_slave(real_dev)) struct net_device *upper_dev;
return __vlan_find_dev_deep(real_dev->master, vlan_id);
upper_dev = netdev_master_upper_dev_get_rcu(dev);
if (upper_dev)
return __vlan_find_dev_deep(upper_dev, vlan_id);
} }
return NULL; return NULL;
......
...@@ -148,7 +148,7 @@ static void del_nbp(struct net_bridge_port *p) ...@@ -148,7 +148,7 @@ static void del_nbp(struct net_bridge_port *p)
netdev_rx_handler_unregister(dev); netdev_rx_handler_unregister(dev);
synchronize_net(); synchronize_net();
netdev_set_master(dev, NULL); netdev_upper_dev_unlink(dev, br->dev);
br_multicast_del_port(p); br_multicast_del_port(p);
...@@ -364,7 +364,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) ...@@ -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)))) if (br_netpoll_info(br) && ((err = br_netpoll_enable(p, GFP_KERNEL))))
goto err3; goto err3;
err = netdev_set_master(dev, br->dev); err = netdev_master_upper_dev_link(dev, br->dev);
if (err) if (err)
goto err4; goto err4;
...@@ -403,7 +403,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev) ...@@ -403,7 +403,7 @@ int br_add_if(struct net_bridge *br, struct net_device *dev)
return 0; return 0;
err5: err5:
netdev_set_master(dev, NULL); netdev_upper_dev_unlink(dev, br->dev);
err4: err4:
br_netpoll_disable(p); br_netpoll_disable(p);
err3: err3:
......
...@@ -4600,64 +4600,231 @@ static int __init dev_proc_init(void) ...@@ -4600,64 +4600,231 @@ static int __init dev_proc_init(void)
#endif /* CONFIG_PROC_FS */ #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 * netdev_has_upper_dev - Check if device is linked to an upper device
* @slave: slave device * @dev: device
* @master: new master device * @upper_dev: upper device to check
* *
* Changes the master device of the slave. Pass %NULL to break the * Find out if a device is linked to specified upper device and return true
* bonding. The caller must hold the RTNL semaphore. On a failure * in case it is. Note that this checks only immediate upper device,
* a negative errno code is returned. On success the reference counts * not through a complete stack of devices. The caller must hold the RTNL lock.
* are adjusted and the function returns zero.
*/ */
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(); ASSERT_RTNL();
if (master) { return !list_empty(&dev->upper_dev_list);
if (old) }
return -EBUSY; EXPORT_SYMBOL(netdev_has_any_upper_dev);
dev_hold(master);
}
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) ASSERT_RTNL();
dev_put(old);
return 0; 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 * netdev_master_upper_dev_get_rcu - Get master upper device
* @slave: slave device * @dev: device
* @master: new master device *
* * Find a master upper device and return pointer to it or NULL in case
* Changes the master device of the slave. Pass %NULL to break the * it's not there. The caller must hold the RCU read lock.
* 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.
*/ */
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(); ASSERT_RTNL();
err = netdev_set_master(slave, master); if (dev == upper_dev)
if (err) return -EBUSY;
return err;
/* 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) if (master)
slave->flags |= IFF_SLAVE; list_add_rcu(&upper->list, &dev->upper_dev_list);
else 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; 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) 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) ...@@ -5503,8 +5670,8 @@ static void rollback_registered_many(struct list_head *head)
if (dev->netdev_ops->ndo_uninit) if (dev->netdev_ops->ndo_uninit)
dev->netdev_ops->ndo_uninit(dev); dev->netdev_ops->ndo_uninit(dev);
/* Notifier chain MUST detach us from master device. */ /* Notifier chain MUST detach us all upper devices. */
WARN_ON(dev->master); WARN_ON(netdev_has_any_upper_dev(dev));
/* Remove entries from kobject tree */ /* Remove entries from kobject tree */
netdev_unregister_kobject(dev); netdev_unregister_kobject(dev);
...@@ -6212,6 +6379,7 @@ struct net_device *alloc_netdev_mqs(int sizeof_priv, const char *name, ...@@ -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->napi_list);
INIT_LIST_HEAD(&dev->unreg_list); INIT_LIST_HEAD(&dev->unreg_list);
INIT_LIST_HEAD(&dev->link_watch_list); INIT_LIST_HEAD(&dev->link_watch_list);
INIT_LIST_HEAD(&dev->upper_dev_list);
dev->priv_flags = IFF_XMIT_DST_RELEASE; dev->priv_flags = IFF_XMIT_DST_RELEASE;
setup(dev); setup(dev);
......
...@@ -210,9 +210,12 @@ static void netpoll_poll_dev(struct net_device *dev) ...@@ -210,9 +210,12 @@ static void netpoll_poll_dev(struct net_device *dev)
if (dev->flags & IFF_SLAVE) { if (dev->flags & IFF_SLAVE) {
if (ni) { if (ni) {
struct net_device *bond_dev = dev->master; struct net_device *bond_dev;
struct sk_buff *skb; 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))) { while ((skb = skb_dequeue(&ni->arp_tx))) {
skb->dev = bond_dev; skb->dev = bond_dev;
skb_queue_tail(&bond_ni->arp_tx, skb); skb_queue_tail(&bond_ni->arp_tx, skb);
...@@ -815,7 +818,7 @@ int netpoll_setup(struct netpoll *np) ...@@ -815,7 +818,7 @@ int netpoll_setup(struct netpoll *np)
return -ENODEV; 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); np_err(np, "%s is a slave device, aborting\n", np->dev_name);
err = -EBUSY; err = -EBUSY;
goto put; goto put;
......
...@@ -880,6 +880,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev, ...@@ -880,6 +880,7 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
const struct rtnl_link_stats64 *stats; const struct rtnl_link_stats64 *stats;
struct nlattr *attr, *af_spec; struct nlattr *attr, *af_spec;
struct rtnl_af_ops *af_ops; struct rtnl_af_ops *af_ops;
struct net_device *upper_dev = netdev_master_upper_dev_get(dev);
ASSERT_RTNL(); ASSERT_RTNL();
nlh = nlmsg_put(skb, pid, seq, type, sizeof(*ifm), flags); 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, ...@@ -908,8 +909,8 @@ static int rtnl_fill_ifinfo(struct sk_buff *skb, struct net_device *dev,
#endif #endif
(dev->ifindex != dev->iflink && (dev->ifindex != dev->iflink &&
nla_put_u32(skb, IFLA_LINK, dev->iflink)) || nla_put_u32(skb, IFLA_LINK, dev->iflink)) ||
(dev->master && (upper_dev &&
nla_put_u32(skb, IFLA_MASTER, dev->master->ifindex)) || nla_put_u32(skb, IFLA_MASTER, upper_dev->ifindex)) ||
nla_put_u8(skb, IFLA_CARRIER, netif_carrier_ok(dev)) || nla_put_u8(skb, IFLA_CARRIER, netif_carrier_ok(dev)) ||
(dev->qdisc && (dev->qdisc &&
nla_put_string(skb, IFLA_QDISC, dev->qdisc->ops->id)) || 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) ...@@ -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) 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; const struct net_device_ops *ops;
int err; int err;
if (dev->master) { if (upper_dev) {
if (dev->master->ifindex == ifindex) if (upper_dev->ifindex == ifindex)
return 0; return 0;
ops = dev->master->netdev_ops; ops = upper_dev->netdev_ops;
if (ops->ndo_del_slave) { if (ops->ndo_del_slave) {
err = ops->ndo_del_slave(dev->master, dev); err = ops->ndo_del_slave(upper_dev, dev);
if (err) if (err)
return err; return err;
} else { } else {
...@@ -1291,12 +1292,12 @@ static int do_set_master(struct net_device *dev, int ifindex) ...@@ -1291,12 +1292,12 @@ static int do_set_master(struct net_device *dev, int ifindex)
} }
if (ifindex) { if (ifindex) {
master_dev = __dev_get_by_index(dev_net(dev), ifindex); upper_dev = __dev_get_by_index(dev_net(dev), ifindex);
if (!master_dev) if (!upper_dev)
return -EINVAL; return -EINVAL;
ops = master_dev->netdev_ops; ops = upper_dev->netdev_ops;
if (ops->ndo_add_slave) { if (ops->ndo_add_slave) {
err = ops->ndo_add_slave(master_dev, dev); err = ops->ndo_add_slave(upper_dev, dev);
if (err) if (err)
return err; return err;
} else { } else {
...@@ -1986,6 +1987,7 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change) ...@@ -1986,6 +1987,7 @@ void rtmsg_ifinfo(int type, struct net_device *dev, unsigned int change)
if (err < 0) if (err < 0)
rtnl_set_sk_err(net, RTNLGRP_LINK, err); rtnl_set_sk_err(net, RTNLGRP_LINK, err);
} }
EXPORT_SYMBOL(rtmsg_ifinfo);
static int nlmsg_populate_fdb_fill(struct sk_buff *skb, static int nlmsg_populate_fdb_fill(struct sk_buff *skb,
struct net_device *dev, struct net_device *dev,
...@@ -2048,7 +2050,6 @@ static void rtnl_fdb_notify(struct net_device *dev, u8 *addr, int type) ...@@ -2048,7 +2050,6 @@ static void rtnl_fdb_notify(struct net_device *dev, u8 *addr, int type)
static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg)
{ {
struct net *net = sock_net(skb->sk); struct net *net = sock_net(skb->sk);
struct net_device *master = NULL;
struct ndmsg *ndm; struct ndmsg *ndm;
struct nlattr *tb[NDA_MAX+1]; struct nlattr *tb[NDA_MAX+1];
struct net_device *dev; struct net_device *dev;
...@@ -2090,10 +2091,10 @@ static int rtnl_fdb_add(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) ...@@ -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 */ /* Support fdb on master device the net/bridge default case */
if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) && if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) &&
(dev->priv_flags & IFF_BRIDGE_PORT)) { (dev->priv_flags & IFF_BRIDGE_PORT)) {
master = dev->master; struct net_device *br_dev = netdev_master_upper_dev_get(dev);
err = master->netdev_ops->ndo_fdb_add(ndm, tb, const struct net_device_ops *ops = br_dev->netdev_ops;
dev, addr,
nlh->nlmsg_flags); err = ops->ndo_fdb_add(ndm, tb, dev, addr, nlh->nlmsg_flags);
if (err) if (err)
goto out; goto out;
else else
...@@ -2154,10 +2155,11 @@ static int rtnl_fdb_del(struct sk_buff *skb, struct nlmsghdr *nlh, void *arg) ...@@ -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 */ /* Support fdb on master device the net/bridge default case */
if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) && if ((!ndm->ndm_flags || ndm->ndm_flags & NTF_MASTER) &&
(dev->priv_flags & IFF_BRIDGE_PORT)) { (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) if (ops->ndo_fdb_del)
err = master->netdev_ops->ndo_fdb_del(ndm, dev, addr); err = ops->ndo_fdb_del(ndm, dev, addr);
if (err) if (err)
goto out; goto out;
...@@ -2241,9 +2243,11 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -2241,9 +2243,11 @@ static int rtnl_fdb_dump(struct sk_buff *skb, struct netlink_callback *cb)
rcu_read_lock(); rcu_read_lock();
for_each_netdev_rcu(net, dev) { for_each_netdev_rcu(net, dev) {
if (dev->priv_flags & IFF_BRIDGE_PORT) { if (dev->priv_flags & IFF_BRIDGE_PORT) {
struct net_device *master = dev->master; struct net_device *br_dev;
const struct net_device_ops *ops = master->netdev_ops; const struct net_device_ops *ops;
br_dev = netdev_master_upper_dev_get(dev);
ops = br_dev->netdev_ops;
if (ops->ndo_fdb_dump) if (ops->ndo_fdb_dump)
idx = ops->ndo_fdb_dump(skb, cb, dev, idx); 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, ...@@ -2264,6 +2268,7 @@ int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq,
struct ifinfomsg *ifm; struct ifinfomsg *ifm;
struct nlattr *br_afspec; struct nlattr *br_afspec;
u8 operstate = netif_running(dev) ? dev->operstate : IF_OPER_DOWN; 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); nlh = nlmsg_put(skb, pid, seq, RTM_NEWLINK, sizeof(*ifm), NLM_F_MULTI);
if (nlh == NULL) if (nlh == NULL)
...@@ -2281,8 +2286,8 @@ int ndo_dflt_bridge_getlink(struct sk_buff *skb, u32 pid, u32 seq, ...@@ -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) || if (nla_put_string(skb, IFLA_IFNAME, dev->name) ||
nla_put_u32(skb, IFLA_MTU, dev->mtu) || nla_put_u32(skb, IFLA_MTU, dev->mtu) ||
nla_put_u8(skb, IFLA_OPERSTATE, operstate) || nla_put_u8(skb, IFLA_OPERSTATE, operstate) ||
(dev->master && (br_dev &&
nla_put_u32(skb, IFLA_MASTER, dev->master->ifindex)) || nla_put_u32(skb, IFLA_MASTER, br_dev->ifindex)) ||
(dev->addr_len && (dev->addr_len &&
nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) || nla_put(skb, IFLA_ADDRESS, dev->addr_len, dev->dev_addr)) ||
(dev->ifindex != dev->iflink && (dev->ifindex != dev->iflink &&
...@@ -2318,11 +2323,11 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb) ...@@ -2318,11 +2323,11 @@ static int rtnl_bridge_getlink(struct sk_buff *skb, struct netlink_callback *cb)
rcu_read_lock(); rcu_read_lock();
for_each_netdev_rcu(net, dev) { for_each_netdev_rcu(net, dev) {
const struct net_device_ops *ops = dev->netdev_ops; 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] && if (idx >= cb->args[0] &&
master->netdev_ops->ndo_bridge_getlink( br_dev->netdev_ops->ndo_bridge_getlink(
skb, portid, seq, dev) < 0) skb, portid, seq, dev) < 0)
break; break;
idx++; idx++;
...@@ -2359,7 +2364,7 @@ static inline size_t bridge_nlmsg_size(void) ...@@ -2359,7 +2364,7 @@ static inline size_t bridge_nlmsg_size(void)
static int rtnl_bridge_notify(struct net_device *dev, u16 flags) static int rtnl_bridge_notify(struct net_device *dev, u16 flags)
{ {
struct net *net = dev_net(dev); 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; struct sk_buff *skb;
int err = -EOPNOTSUPP; int err = -EOPNOTSUPP;
...@@ -2370,8 +2375,8 @@ static int rtnl_bridge_notify(struct net_device *dev, u16 flags) ...@@ -2370,8 +2375,8 @@ static int rtnl_bridge_notify(struct net_device *dev, u16 flags)
} }
if ((!flags || (flags & BRIDGE_FLAGS_MASTER)) && if ((!flags || (flags & BRIDGE_FLAGS_MASTER)) &&
master && master->netdev_ops->ndo_bridge_getlink) { br_dev && br_dev->netdev_ops->ndo_bridge_getlink) {
err = master->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev); err = br_dev->netdev_ops->ndo_bridge_getlink(skb, 0, 0, dev);
if (err < 0) if (err < 0)
goto errout; goto errout;
} }
...@@ -2430,13 +2435,14 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh, ...@@ -2430,13 +2435,14 @@ static int rtnl_bridge_setlink(struct sk_buff *skb, struct nlmsghdr *nlh,
oflags = flags; oflags = flags;
if (!flags || (flags & BRIDGE_FLAGS_MASTER)) { if (!flags || (flags & BRIDGE_FLAGS_MASTER)) {
if (!dev->master || struct net_device *br_dev = netdev_master_upper_dev_get(dev);
!dev->master->netdev_ops->ndo_bridge_setlink) {
if (!br_dev || !br_dev->netdev_ops->ndo_bridge_setlink) {
err = -EOPNOTSUPP; err = -EOPNOTSUPP;
goto out; goto out;
} }
err = dev->master->netdev_ops->ndo_bridge_setlink(dev, nlh); err = br_dev->netdev_ops->ndo_bridge_setlink(dev, nlh);
if (err) if (err)
goto out; goto out;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment