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 /net | |
| 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>
Diffstat (limited to 'net')
| -rw-r--r-- | net/core/dev.c | 163 | ||||
| -rw-r--r-- | net/core/rtnetlink.c | 2 |
2 files changed, 104 insertions, 61 deletions
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: |
