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>
This commit is contained in:
commit
912df2628b
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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:
|
||||
|
|
258
net/core/dev.c
258
net/core/dev.c
|
@ -4600,64 +4600,231 @@ static int __init dev_proc_init(void)
|
|||
#endif /* CONFIG_PROC_FS */
|
||||
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
int netdev_set_master(struct net_device *slave, struct net_device *master)
|
||||
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 net_device *old = slave->master;
|
||||
struct netdev_upper *upper;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
if (master) {
|
||||
if (old)
|
||||
return -EBUSY;
|
||||
dev_hold(master);
|
||||
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);
|
||||
}
|
||||
|
||||
slave->master = master;
|
||||
|
||||
if (old)
|
||||
dev_put(old);
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(netdev_set_master);
|
||||
|
||||
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_bond_master - set up bonding master/slave pair
|
||||
* @slave: slave device
|
||||
* @master: new master device
|
||||
* netdev_has_upper_dev - Check if device is linked to an upper device
|
||||
* @dev: device
|
||||
* @upper_dev: upper device to check
|
||||
*
|
||||
* 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.
|
||||
* 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_bond_master(struct net_device *slave, struct net_device *master)
|
||||
bool netdev_has_upper_dev(struct net_device *dev,
|
||||
struct net_device *upper_dev)
|
||||
{
|
||||
int err;
|
||||
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();
|
||||
|
||||
return !list_empty(&dev->upper_dev_list);
|
||||
}
|
||||
EXPORT_SYMBOL(netdev_has_any_upper_dev);
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
ASSERT_RTNL();
|
||||
|
||||
err = netdev_set_master(slave, master);
|
||||
if (err)
|
||||
return err;
|
||||
if (master)
|
||||
slave->flags |= IFF_SLAVE;
|
||||
else
|
||||
slave->flags &= ~IFF_SLAVE;
|
||||
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_master_upper_dev_get);
|
||||
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
struct net_device *netdev_master_upper_dev_get_rcu(struct net_device *dev)
|
||||
{
|
||||
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();
|
||||
|
||||
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)
|
||||
list_add_rcu(&upper->list, &dev->upper_dev_list);
|
||||
else
|
||||
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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
Loading…
Reference in New Issue