diff options
author | Patrick McHardy <kaber@trash.net> | 2010-02-26 01:34:53 -0500 |
---|---|---|
committer | David S. Miller <davem@davemloft.net> | 2010-02-27 05:43:40 -0500 |
commit | bd38081160bb3d036db98472e537b6a7dd4da51a (patch) | |
tree | 26af1cae5e2dda3269da6f812586e93954582d54 | |
parent | a2835763e130c343ace5320c20d33c281e7097b7 (diff) |
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>
-rw-r--r-- | include/linux/netdevice.h | 2 | ||||
-rw-r--r-- | net/core/dev.c | 163 | ||||
-rw-r--r-- | net/core/rtnetlink.c | 2 |
3 files changed, 106 insertions, 61 deletions
diff --git a/include/linux/netdevice.h b/include/linux/netdevice.h index 1bfda90c2625..c79a88be7c33 100644 --- a/include/linux/netdevice.h +++ b/include/linux/netdevice.h | |||
@@ -1587,7 +1587,9 @@ extern int dev_valid_name(const char *name); | |||
1587 | extern int dev_ioctl(struct net *net, unsigned int cmd, void __user *); | 1587 | extern int dev_ioctl(struct net *net, unsigned int cmd, void __user *); |
1588 | extern int dev_ethtool(struct net *net, struct ifreq *); | 1588 | extern int dev_ethtool(struct net *net, struct ifreq *); |
1589 | extern unsigned dev_get_flags(const struct net_device *); | 1589 | extern unsigned dev_get_flags(const struct net_device *); |
1590 | extern int __dev_change_flags(struct net_device *, unsigned int flags); | ||
1590 | extern int dev_change_flags(struct net_device *, unsigned); | 1591 | extern int dev_change_flags(struct net_device *, unsigned); |
1592 | extern void __dev_notify_flags(struct net_device *, unsigned int old_flags); | ||
1591 | extern int dev_change_name(struct net_device *, const char *); | 1593 | extern int dev_change_name(struct net_device *, const char *); |
1592 | extern int dev_set_alias(struct net_device *, const char *, size_t); | 1594 | extern int dev_set_alias(struct net_device *, const char *, size_t); |
1593 | extern int dev_change_net_namespace(struct net_device *, | 1595 | extern int dev_change_net_namespace(struct net_device *, |
diff --git a/net/core/dev.c b/net/core/dev.c index 75332b089529..e5972f7f7e1b 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -1113,19 +1113,7 @@ void dev_load(struct net *net, const char *name) | |||
1113 | } | 1113 | } |
1114 | EXPORT_SYMBOL(dev_load); | 1114 | EXPORT_SYMBOL(dev_load); |
1115 | 1115 | ||
1116 | /** | 1116 | static int __dev_open(struct net_device *dev) |
1117 | * dev_open - prepare an interface for use. | ||
1118 | * @dev: device to open | ||
1119 | * | ||
1120 | * Takes a device from down to up state. The device's private open | ||
1121 | * function is invoked and then the multicast lists are loaded. Finally | ||
1122 | * the device is moved into the up state and a %NETDEV_UP message is | ||
1123 | * sent to the netdev notifier chain. | ||
1124 | * | ||
1125 | * Calling this function on an active interface is a nop. On a failure | ||
1126 | * a negative errno code is returned. | ||
1127 | */ | ||
1128 | int dev_open(struct net_device *dev) | ||
1129 | { | 1117 | { |
1130 | const struct net_device_ops *ops = dev->netdev_ops; | 1118 | const struct net_device_ops *ops = dev->netdev_ops; |
1131 | int ret; | 1119 | int ret; |
@@ -1133,13 +1121,6 @@ int dev_open(struct net_device *dev) | |||
1133 | ASSERT_RTNL(); | 1121 | ASSERT_RTNL(); |
1134 | 1122 | ||
1135 | /* | 1123 | /* |
1136 | * Is it already up? | ||
1137 | */ | ||
1138 | |||
1139 | if (dev->flags & IFF_UP) | ||
1140 | return 0; | ||
1141 | |||
1142 | /* | ||
1143 | * Is it even present? | 1124 | * Is it even present? |
1144 | */ | 1125 | */ |
1145 | if (!netif_device_present(dev)) | 1126 | if (!netif_device_present(dev)) |
@@ -1187,36 +1168,57 @@ int dev_open(struct net_device *dev) | |||
1187 | * Wakeup transmit queue engine | 1168 | * Wakeup transmit queue engine |
1188 | */ | 1169 | */ |
1189 | dev_activate(dev); | 1170 | dev_activate(dev); |
1190 | |||
1191 | /* | ||
1192 | * ... and announce new interface. | ||
1193 | */ | ||
1194 | call_netdevice_notifiers(NETDEV_UP, dev); | ||
1195 | } | 1171 | } |
1196 | 1172 | ||
1197 | return ret; | 1173 | return ret; |
1198 | } | 1174 | } |
1199 | EXPORT_SYMBOL(dev_open); | ||
1200 | 1175 | ||
1201 | /** | 1176 | /** |
1202 | * dev_close - shutdown an interface. | 1177 | * dev_open - prepare an interface for use. |
1203 | * @dev: device to shutdown | 1178 | * @dev: device to open |
1204 | * | 1179 | * |
1205 | * This function moves an active device into down state. A | 1180 | * Takes a device from down to up state. The device's private open |
1206 | * %NETDEV_GOING_DOWN is sent to the netdev notifier chain. The device | 1181 | * function is invoked and then the multicast lists are loaded. Finally |
1207 | * is then deactivated and finally a %NETDEV_DOWN is sent to the notifier | 1182 | * the device is moved into the up state and a %NETDEV_UP message is |
1208 | * chain. | 1183 | * sent to the netdev notifier chain. |
1184 | * | ||
1185 | * Calling this function on an active interface is a nop. On a failure | ||
1186 | * a negative errno code is returned. | ||
1209 | */ | 1187 | */ |
1210 | int dev_close(struct net_device *dev) | 1188 | int dev_open(struct net_device *dev) |
1189 | { | ||
1190 | int ret; | ||
1191 | |||
1192 | /* | ||
1193 | * Is it already up? | ||
1194 | */ | ||
1195 | if (dev->flags & IFF_UP) | ||
1196 | return 0; | ||
1197 | |||
1198 | /* | ||
1199 | * Open device | ||
1200 | */ | ||
1201 | ret = __dev_open(dev); | ||
1202 | if (ret < 0) | ||
1203 | return ret; | ||
1204 | |||
1205 | /* | ||
1206 | * ... and announce new interface. | ||
1207 | */ | ||
1208 | rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING); | ||
1209 | call_netdevice_notifiers(NETDEV_UP, dev); | ||
1210 | |||
1211 | return ret; | ||
1212 | } | ||
1213 | EXPORT_SYMBOL(dev_open); | ||
1214 | |||
1215 | static int __dev_close(struct net_device *dev) | ||
1211 | { | 1216 | { |
1212 | const struct net_device_ops *ops = dev->netdev_ops; | 1217 | const struct net_device_ops *ops = dev->netdev_ops; |
1213 | ASSERT_RTNL(); | ||
1214 | 1218 | ||
1219 | ASSERT_RTNL(); | ||
1215 | might_sleep(); | 1220 | might_sleep(); |
1216 | 1221 | ||
1217 | if (!(dev->flags & IFF_UP)) | ||
1218 | return 0; | ||
1219 | |||
1220 | /* | 1222 | /* |
1221 | * Tell people we are going down, so that they can | 1223 | * Tell people we are going down, so that they can |
1222 | * prepare to death, when device is still operating. | 1224 | * prepare to death, when device is still operating. |
@@ -1252,14 +1254,34 @@ int dev_close(struct net_device *dev) | |||
1252 | dev->flags &= ~IFF_UP; | 1254 | dev->flags &= ~IFF_UP; |
1253 | 1255 | ||
1254 | /* | 1256 | /* |
1255 | * Tell people we are down | 1257 | * Shutdown NET_DMA |
1256 | */ | 1258 | */ |
1257 | call_netdevice_notifiers(NETDEV_DOWN, dev); | 1259 | net_dmaengine_put(); |
1260 | |||
1261 | return 0; | ||
1262 | } | ||
1263 | |||
1264 | /** | ||
1265 | * dev_close - shutdown an interface. | ||
1266 | * @dev: device to shutdown | ||
1267 | * | ||
1268 | * This function moves an active device into down state. A | ||
1269 | * %NETDEV_GOING_DOWN is sent to the netdev notifier chain. The device | ||
1270 | * is then deactivated and finally a %NETDEV_DOWN is sent to the notifier | ||
1271 | * chain. | ||
1272 | */ | ||
1273 | int dev_close(struct net_device *dev) | ||
1274 | { | ||
1275 | if (!(dev->flags & IFF_UP)) | ||
1276 | return 0; | ||
1277 | |||
1278 | __dev_close(dev); | ||
1258 | 1279 | ||
1259 | /* | 1280 | /* |
1260 | * Shutdown NET_DMA | 1281 | * Tell people we are down |
1261 | */ | 1282 | */ |
1262 | net_dmaengine_put(); | 1283 | rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING); |
1284 | call_netdevice_notifiers(NETDEV_DOWN, dev); | ||
1263 | 1285 | ||
1264 | return 0; | 1286 | return 0; |
1265 | } | 1287 | } |
@@ -4299,18 +4321,10 @@ unsigned dev_get_flags(const struct net_device *dev) | |||
4299 | } | 4321 | } |
4300 | EXPORT_SYMBOL(dev_get_flags); | 4322 | EXPORT_SYMBOL(dev_get_flags); |
4301 | 4323 | ||
4302 | /** | 4324 | int __dev_change_flags(struct net_device *dev, unsigned int flags) |
4303 | * dev_change_flags - change device settings | ||
4304 | * @dev: device | ||
4305 | * @flags: device state flags | ||
4306 | * | ||
4307 | * Change settings on device based state flags. The flags are | ||
4308 | * in the userspace exported format. | ||
4309 | */ | ||
4310 | int dev_change_flags(struct net_device *dev, unsigned flags) | ||
4311 | { | 4325 | { |
4312 | int ret, changes; | ||
4313 | int old_flags = dev->flags; | 4326 | int old_flags = dev->flags; |
4327 | int ret; | ||
4314 | 4328 | ||
4315 | ASSERT_RTNL(); | 4329 | ASSERT_RTNL(); |
4316 | 4330 | ||
@@ -4341,17 +4355,12 @@ int dev_change_flags(struct net_device *dev, unsigned flags) | |||
4341 | 4355 | ||
4342 | ret = 0; | 4356 | ret = 0; |
4343 | if ((old_flags ^ flags) & IFF_UP) { /* Bit is different ? */ | 4357 | if ((old_flags ^ flags) & IFF_UP) { /* Bit is different ? */ |
4344 | ret = ((old_flags & IFF_UP) ? dev_close : dev_open)(dev); | 4358 | ret = ((old_flags & IFF_UP) ? __dev_close : __dev_open)(dev); |
4345 | 4359 | ||
4346 | if (!ret) | 4360 | if (!ret) |
4347 | dev_set_rx_mode(dev); | 4361 | dev_set_rx_mode(dev); |
4348 | } | 4362 | } |
4349 | 4363 | ||
4350 | if (dev->flags & IFF_UP && | ||
4351 | ((old_flags ^ dev->flags) & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI | | ||
4352 | IFF_VOLATILE))) | ||
4353 | call_netdevice_notifiers(NETDEV_CHANGE, dev); | ||
4354 | |||
4355 | if ((flags ^ dev->gflags) & IFF_PROMISC) { | 4364 | if ((flags ^ dev->gflags) & IFF_PROMISC) { |
4356 | int inc = (flags & IFF_PROMISC) ? 1 : -1; | 4365 | int inc = (flags & IFF_PROMISC) ? 1 : -1; |
4357 | 4366 | ||
@@ -4370,11 +4379,47 @@ int dev_change_flags(struct net_device *dev, unsigned flags) | |||
4370 | dev_set_allmulti(dev, inc); | 4379 | dev_set_allmulti(dev, inc); |
4371 | } | 4380 | } |
4372 | 4381 | ||
4373 | /* Exclude state transition flags, already notified */ | 4382 | return ret; |
4374 | changes = (old_flags ^ dev->flags) & ~(IFF_UP | IFF_RUNNING); | 4383 | } |
4384 | |||
4385 | void __dev_notify_flags(struct net_device *dev, unsigned int old_flags) | ||
4386 | { | ||
4387 | unsigned int changes = dev->flags ^ old_flags; | ||
4388 | |||
4389 | if (changes & IFF_UP) { | ||
4390 | if (dev->flags & IFF_UP) | ||
4391 | call_netdevice_notifiers(NETDEV_UP, dev); | ||
4392 | else | ||
4393 | call_netdevice_notifiers(NETDEV_DOWN, dev); | ||
4394 | } | ||
4395 | |||
4396 | if (dev->flags & IFF_UP && | ||
4397 | (changes & ~(IFF_UP | IFF_PROMISC | IFF_ALLMULTI | IFF_VOLATILE))) | ||
4398 | call_netdevice_notifiers(NETDEV_CHANGE, dev); | ||
4399 | } | ||
4400 | |||
4401 | /** | ||
4402 | * dev_change_flags - change device settings | ||
4403 | * @dev: device | ||
4404 | * @flags: device state flags | ||
4405 | * | ||
4406 | * Change settings on device based state flags. The flags are | ||
4407 | * in the userspace exported format. | ||
4408 | */ | ||
4409 | int dev_change_flags(struct net_device *dev, unsigned flags) | ||
4410 | { | ||
4411 | int ret, changes; | ||
4412 | int old_flags = dev->flags; | ||
4413 | |||
4414 | ret = __dev_change_flags(dev, flags); | ||
4415 | if (ret < 0) | ||
4416 | return ret; | ||
4417 | |||
4418 | changes = old_flags ^ dev->flags; | ||
4375 | if (changes) | 4419 | if (changes) |
4376 | rtmsg_ifinfo(RTM_NEWLINK, dev, changes); | 4420 | rtmsg_ifinfo(RTM_NEWLINK, dev, changes); |
4377 | 4421 | ||
4422 | __dev_notify_flags(dev, old_flags); | ||
4378 | return ret; | 4423 | return ret; |
4379 | } | 4424 | } |
4380 | EXPORT_SYMBOL(dev_change_flags); | 4425 | EXPORT_SYMBOL(dev_change_flags); |
diff --git a/net/core/rtnetlink.c b/net/core/rtnetlink.c index 020e43bfef5f..c21ec4236dd0 100644 --- a/net/core/rtnetlink.c +++ b/net/core/rtnetlink.c | |||
@@ -1427,8 +1427,6 @@ static int rtnetlink_event(struct notifier_block *this, unsigned long event, voi | |||
1427 | switch (event) { | 1427 | switch (event) { |
1428 | case NETDEV_UP: | 1428 | case NETDEV_UP: |
1429 | case NETDEV_DOWN: | 1429 | case NETDEV_DOWN: |
1430 | rtmsg_ifinfo(RTM_NEWLINK, dev, IFF_UP|IFF_RUNNING); | ||
1431 | break; | ||
1432 | case NETDEV_PRE_UP: | 1430 | case NETDEV_PRE_UP: |
1433 | case NETDEV_POST_INIT: | 1431 | case NETDEV_POST_INIT: |
1434 | case NETDEV_REGISTER: | 1432 | case NETDEV_REGISTER: |