dev: support deferring device flag change notifications
Split dev_change_flags() into two functions: __dev_change_flags() to perform the actual changes and __dev_notify_flags() to invoke netdevice notifiers. This will be used by rtnl_link to defer netlink notifications until the device has been fully configured. This changes ordering of some operations, in particular: - netlink notifications are sent after all changes have been performed. As a side effect this surpresses one unnecessary netlink message when the IFF_UP and other flags are changed simultaneously. - The NETDEV_UP/NETDEV_DOWN and NETDEV_CHANGE notifiers are invoked after all changes have been performed. Their relative is unchanged. - net_dmaengine_put() is invoked before the NETDEV_DOWN notifier instead of afterwards. This should not make any difference since both RX and TX are already shut down at this point. Signed-off-by: Patrick McHardy <kaber@trash.net> Signed-off-by: David S. Miller <davem@davemloft.net>
This commit is contained in:
committed by
David S. Miller
parent
a2835763e1
commit
bd38081160
@@ -1587,7 +1587,9 @@ extern int dev_valid_name(const char *name);
|
|||||||
extern int dev_ioctl(struct net *net, unsigned int cmd, void __user *);
|
extern int dev_ioctl(struct net *net, unsigned int cmd, void __user *);
|
||||||
extern int dev_ethtool(struct net *net, struct ifreq *);
|
extern int dev_ethtool(struct net *net, struct ifreq *);
|
||||||
extern unsigned dev_get_flags(const struct net_device *);
|
extern unsigned dev_get_flags(const struct net_device *);
|
||||||
|
extern int __dev_change_flags(struct net_device *, unsigned int flags);
|
||||||
extern int dev_change_flags(struct net_device *, unsigned);
|
extern int dev_change_flags(struct net_device *, unsigned);
|
||||||
|
extern void __dev_notify_flags(struct net_device *, unsigned int old_flags);
|
||||||
extern int dev_change_name(struct net_device *, const char *);
|
extern int dev_change_name(struct net_device *, const char *);
|
||||||
extern int dev_set_alias(struct net_device *, const char *, size_t);
|
extern int dev_set_alias(struct net_device *, const char *, size_t);
|
||||||
extern int dev_change_net_namespace(struct net_device *,
|
extern int dev_change_net_namespace(struct net_device *,
|
||||||
|
169
net/core/dev.c
169
net/core/dev.c
@@ -1113,32 +1113,13 @@ void dev_load(struct net *net, const char *name)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(dev_load);
|
EXPORT_SYMBOL(dev_load);
|
||||||
|
|
||||||
/**
|
static int __dev_open(struct net_device *dev)
|
||||||
* dev_open - prepare an interface for use.
|
|
||||||
* @dev: device to open
|
|
||||||
*
|
|
||||||
* Takes a device from down to up state. The device's private open
|
|
||||||
* function is invoked and then the multicast lists are loaded. Finally
|
|
||||||
* the device is moved into the up state and a %NETDEV_UP message is
|
|
||||||
* sent to the netdev notifier chain.
|
|
||||||
*
|
|
||||||
* Calling this function on an active interface is a nop. On a failure
|
|
||||||
* a negative errno code is returned.
|
|
||||||
*/
|
|
||||||
int dev_open(struct net_device *dev)
|
|
||||||
{
|
{
|
||||||
const struct net_device_ops *ops = dev->netdev_ops;
|
const struct net_device_ops *ops = dev->netdev_ops;
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
ASSERT_RTNL();
|
ASSERT_RTNL();
|
||||||
|
|
||||||
/*
|
|
||||||
* Is it already up?
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (dev->flags & IFF_UP)
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Is it even present?
|
* Is it even present?
|
||||||
*/
|
*/
|
||||||
@@ -1187,36 +1168,57 @@ int dev_open(struct net_device *dev)
|
|||||||
* Wakeup transmit queue engine
|
* Wakeup transmit queue engine
|
||||||
*/
|
*/
|
||||||
dev_activate(dev);
|
dev_activate(dev);
|
||||||
|
|
||||||
/*
|
|
||||||
* ... and announce new interface.
|
|
||||||
*/
|
|
||||||
call_netdevice_notifiers(NETDEV_UP, dev);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dev_open - prepare an interface for use.
|
||||||
|
* @dev: device to open
|
||||||
|
*
|
||||||
|
* Takes a device from down to up state. The device's private open
|
||||||
|
* function is invoked and then the multicast lists are loaded. Finally
|
||||||
|
* the device is moved into the up state and a %NETDEV_UP message is
|
||||||
|
* sent to the netdev notifier chain.
|
||||||
|
*
|
||||||
|
* Calling this function on an active interface is a nop. On a failure
|
||||||
|
* a negative errno code is returned.
|
||||||
|
*/
|
||||||
|
int dev_open(struct net_device *dev)
|
||||||
|
{
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Is it already up?
|
||||||
|
*/
|
||||||
|
if (dev->flags & IFF_UP)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Open device
|
||||||
|
*/
|
||||||
|
ret = __dev_open(dev);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* ... and announce new interface.
|
||||||
|
*/
|
||||||
|
rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING);
|
||||||
|
call_netdevice_notifiers(NETDEV_UP, dev);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(dev_open);
|
EXPORT_SYMBOL(dev_open);
|
||||||
|
|
||||||
/**
|
static int __dev_close(struct net_device *dev)
|
||||||
* dev_close - shutdown an interface.
|
|
||||||
* @dev: device to shutdown
|
|
||||||
*
|
|
||||||
* This function moves an active device into down state. A
|
|
||||||
* %NETDEV_GOING_DOWN is sent to the netdev notifier chain. The device
|
|
||||||
* is then deactivated and finally a %NETDEV_DOWN is sent to the notifier
|
|
||||||
* chain.
|
|
||||||
*/
|
|
||||||
int dev_close(struct net_device *dev)
|
|
||||||
{
|
{
|
||||||
const struct net_device_ops *ops = dev->netdev_ops;
|
const struct net_device_ops *ops = dev->netdev_ops;
|
||||||
|
|
||||||
ASSERT_RTNL();
|
ASSERT_RTNL();
|
||||||
|
|
||||||
might_sleep();
|
might_sleep();
|
||||||
|
|
||||||
if (!(dev->flags & IFF_UP))
|
|
||||||
return 0;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Tell people we are going down, so that they can
|
* Tell people we are going down, so that they can
|
||||||
* prepare to death, when device is still operating.
|
* prepare to death, when device is still operating.
|
||||||
@@ -1251,11 +1253,6 @@ int dev_close(struct net_device *dev)
|
|||||||
|
|
||||||
dev->flags &= ~IFF_UP;
|
dev->flags &= ~IFF_UP;
|
||||||
|
|
||||||
/*
|
|
||||||
* Tell people we are down
|
|
||||||
*/
|
|
||||||
call_netdevice_notifiers(NETDEV_DOWN, dev);
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Shutdown NET_DMA
|
* Shutdown NET_DMA
|
||||||
*/
|
*/
|
||||||
@@ -1263,6 +1260,31 @@ int dev_close(struct net_device *dev)
|
|||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dev_close - shutdown an interface.
|
||||||
|
* @dev: device to shutdown
|
||||||
|
*
|
||||||
|
* This function moves an active device into down state. A
|
||||||
|
* %NETDEV_GOING_DOWN is sent to the netdev notifier chain. The device
|
||||||
|
* is then deactivated and finally a %NETDEV_DOWN is sent to the notifier
|
||||||
|
* chain.
|
||||||
|
*/
|
||||||
|
int dev_close(struct net_device *dev)
|
||||||
|
{
|
||||||
|
if (!(dev->flags & IFF_UP))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
__dev_close(dev);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Tell people we are down
|
||||||
|
*/
|
||||||
|
rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING);
|
||||||
|
call_netdevice_notifiers(NETDEV_DOWN, dev);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
EXPORT_SYMBOL(dev_close);
|
EXPORT_SYMBOL(dev_close);
|
||||||
|
|
||||||
|
|
||||||
@@ -4299,18 +4321,10 @@ unsigned dev_get_flags(const struct net_device *dev)
|
|||||||
}
|
}
|
||||||
EXPORT_SYMBOL(dev_get_flags);
|
EXPORT_SYMBOL(dev_get_flags);
|
||||||
|
|
||||||
/**
|
int __dev_change_flags(struct net_device *dev, unsigned int flags)
|
||||||
* dev_change_flags - change device settings
|
|
||||||
* @dev: device
|
|
||||||
* @flags: device state flags
|
|
||||||
*
|
|
||||||
* Change settings on device based state flags. The flags are
|
|
||||||
* in the userspace exported format.
|
|
||||||
*/
|
|
||||||
int dev_change_flags(struct net_device *dev, unsigned flags)
|
|
||||||
{
|
{
|
||||||
int ret, changes;
|
|
||||||
int old_flags = dev->flags;
|
int old_flags = dev->flags;
|
||||||
|
int ret;
|
||||||
|
|
||||||
ASSERT_RTNL();
|
ASSERT_RTNL();
|
||||||
|
|
||||||
@@ -4341,17 +4355,12 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
|
|||||||
|
|
||||||
ret = 0;
|
ret = 0;
|
||||||
if ((old_flags ^ flags) & IFF_UP) { /* Bit is different ? */
|
if ((old_flags ^ flags) & IFF_UP) { /* Bit is different ? */
|
||||||
ret = ((old_flags & IFF_UP) ? dev_close : dev_open)(dev);
|
ret = ((old_flags & IFF_UP) ? __dev_close : __dev_open)(dev);
|
||||||
|
|
||||||
if (!ret)
|
if (!ret)
|
||||||
dev_set_rx_mode(dev);
|
dev_set_rx_mode(dev);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (dev->flags & IFF_UP &&
|
|
||||||
((old_flags ^ dev->flags) & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI |
|
|
||||||
IFF_VOLATILE)))
|
|
||||||
call_netdevice_notifiers(NETDEV_CHANGE, dev);
|
|
||||||
|
|
||||||
if ((flags ^ dev->gflags) & IFF_PROMISC) {
|
if ((flags ^ dev->gflags) & IFF_PROMISC) {
|
||||||
int inc = (flags & IFF_PROMISC) ? 1 : -1;
|
int inc = (flags & IFF_PROMISC) ? 1 : -1;
|
||||||
|
|
||||||
@@ -4370,11 +4379,47 @@ int dev_change_flags(struct net_device *dev, unsigned flags)
|
|||||||
dev_set_allmulti(dev, inc);
|
dev_set_allmulti(dev, inc);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Exclude state transition flags, already notified */
|
return ret;
|
||||||
changes = (old_flags ^ dev->flags) & ~(IFF_UP | IFF_RUNNING);
|
}
|
||||||
|
|
||||||
|
void __dev_notify_flags(struct net_device *dev, unsigned int old_flags)
|
||||||
|
{
|
||||||
|
unsigned int changes = dev->flags ^ old_flags;
|
||||||
|
|
||||||
|
if (changes & IFF_UP) {
|
||||||
|
if (dev->flags & IFF_UP)
|
||||||
|
call_netdevice_notifiers(NETDEV_UP, dev);
|
||||||
|
else
|
||||||
|
call_netdevice_notifiers(NETDEV_DOWN, dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dev->flags & IFF_UP &&
|
||||||
|
(changes & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI | IFF_VOLATILE)))
|
||||||
|
call_netdevice_notifiers(NETDEV_CHANGE, dev);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* dev_change_flags - change device settings
|
||||||
|
* @dev: device
|
||||||
|
* @flags: device state flags
|
||||||
|
*
|
||||||
|
* Change settings on device based state flags. The flags are
|
||||||
|
* in the userspace exported format.
|
||||||
|
*/
|
||||||
|
int dev_change_flags(struct net_device *dev, unsigned flags)
|
||||||
|
{
|
||||||
|
int ret, changes;
|
||||||
|
int old_flags = dev->flags;
|
||||||
|
|
||||||
|
ret = __dev_change_flags(dev, flags);
|
||||||
|
if (ret < 0)
|
||||||
|
return ret;
|
||||||
|
|
||||||
|
changes = old_flags ^ dev->flags;
|
||||||
if (changes)
|
if (changes)
|
||||||
rtmsg_ifinfo(RTM_NEWLINK, dev, changes);
|
rtmsg_ifinfo(RTM_NEWLINK, dev, changes);
|
||||||
|
|
||||||
|
__dev_notify_flags(dev, old_flags);
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
EXPORT_SYMBOL(dev_change_flags);
|
EXPORT_SYMBOL(dev_change_flags);
|
||||||
|
@@ -1427,8 +1427,6 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi
|
|||||||
switch (event) {
|
switch (event) {
|
||||||
case NETDEV_UP:
|
case NETDEV_UP:
|
||||||
case NETDEV_DOWN:
|
case NETDEV_DOWN:
|
||||||
rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING);
|
|
||||||
break;
|
|
||||||
case NETDEV_PRE_UP:
|
case NETDEV_PRE_UP:
|
||||||
case NETDEV_POST_INIT:
|
case NETDEV_POST_INIT:
|
||||||
case NETDEV_REGISTER:
|
case NETDEV_REGISTER:
|
||||||
|
Reference in New Issue
Block a user