diff options
author | Herbert Xu <herbert@gondor.apana.org.au> | 2007-07-30 20:03:38 -0400 |
---|---|---|
committer | David S. Miller <davem@sunset.davemloft.net> | 2007-07-31 05:28:15 -0400 |
commit | fcc5a03ac42564e9e255c1134dda47442289e466 (patch) | |
tree | 8be87d6dc1a58b8ccd27de930c17bc1c9c26bac9 /net/core/dev.c | |
parent | aeed9e82cd258b9699eaa6568efefba9cc6d5f01 (diff) |
[NET]: Allow netdev REGISTER/CHANGENAME events to fail
This patch adds code to allow errors to be passed up from event
handlers of NETDEV_REGISTER and NETDEV_CHANGENAME. It also adds
the notifier_from_errno/notifier_to_errnor helpers to pass the
errno value up to the notifier caller.
If an error is detected when a device is registered, it causes
that operation to fail. A NETDEV_UNREGISTER will be sent to
all event handlers.
Similarly if NETDEV_CHANGENAME fails the original name is restored
and a new NETDEV_CHANGENAME event is sent.
As such all event handlers must be idempotent with respect to
these events.
When an event handler is registered NETDEV_REGISTER events are
sent for all devices currently registered. Should any of them
fail, we will send NETDEV_GOING_DOWN/NETDEV_DOWN/NETDEV_UNREGISTER
events to that handler for the devices which have already been
registered with it. The handler registration itself will fail.
Signed-off-by: Herbert Xu <herbert@gondor.apana.org.au>
Signed-off-by: David S. Miller <davem@davemloft.net>
Diffstat (limited to 'net/core/dev.c')
-rw-r--r-- | net/core/dev.c | 62 |
1 files changed, 52 insertions, 10 deletions
diff --git a/net/core/dev.c b/net/core/dev.c index 346cbf66534e..6cc8a70350ac 100644 --- a/net/core/dev.c +++ b/net/core/dev.c | |||
@@ -817,7 +817,9 @@ int dev_alloc_name(struct net_device *dev, const char *name) | |||
817 | */ | 817 | */ |
818 | int dev_change_name(struct net_device *dev, char *newname) | 818 | int dev_change_name(struct net_device *dev, char *newname) |
819 | { | 819 | { |
820 | char oldname[IFNAMSIZ]; | ||
820 | int err = 0; | 821 | int err = 0; |
822 | int ret; | ||
821 | 823 | ||
822 | ASSERT_RTNL(); | 824 | ASSERT_RTNL(); |
823 | 825 | ||
@@ -827,6 +829,8 @@ int dev_change_name(struct net_device *dev, char *newname) | |||
827 | if (!dev_valid_name(newname)) | 829 | if (!dev_valid_name(newname)) |
828 | return -EINVAL; | 830 | return -EINVAL; |
829 | 831 | ||
832 | memcpy(oldname, dev->name, IFNAMSIZ); | ||
833 | |||
830 | if (strchr(newname, '%')) { | 834 | if (strchr(newname, '%')) { |
831 | err = dev_alloc_name(dev, newname); | 835 | err = dev_alloc_name(dev, newname); |
832 | if (err < 0) | 836 | if (err < 0) |
@@ -838,6 +842,7 @@ int dev_change_name(struct net_device *dev, char *newname) | |||
838 | else | 842 | else |
839 | strlcpy(dev->name, newname, IFNAMSIZ); | 843 | strlcpy(dev->name, newname, IFNAMSIZ); |
840 | 844 | ||
845 | rollback: | ||
841 | device_rename(&dev->dev, dev->name); | 846 | device_rename(&dev->dev, dev->name); |
842 | 847 | ||
843 | write_lock_bh(&dev_base_lock); | 848 | write_lock_bh(&dev_base_lock); |
@@ -845,7 +850,20 @@ int dev_change_name(struct net_device *dev, char *newname) | |||
845 | hlist_add_head(&dev->name_hlist, dev_name_hash(dev->name)); | 850 | hlist_add_head(&dev->name_hlist, dev_name_hash(dev->name)); |
846 | write_unlock_bh(&dev_base_lock); | 851 | write_unlock_bh(&dev_base_lock); |
847 | 852 | ||
848 | raw_notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev); | 853 | ret = raw_notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev); |
854 | ret = notifier_to_errno(ret); | ||
855 | |||
856 | if (ret) { | ||
857 | if (err) { | ||
858 | printk(KERN_ERR | ||
859 | "%s: name change rollback failed: %d.\n", | ||
860 | dev->name, ret); | ||
861 | } else { | ||
862 | err = ret; | ||
863 | memcpy(dev->name, oldname, IFNAMSIZ); | ||
864 | goto rollback; | ||
865 | } | ||
866 | } | ||
849 | 867 | ||
850 | return err; | 868 | return err; |
851 | } | 869 | } |
@@ -1058,20 +1076,43 @@ int dev_close(struct net_device *dev) | |||
1058 | int register_netdevice_notifier(struct notifier_block *nb) | 1076 | int register_netdevice_notifier(struct notifier_block *nb) |
1059 | { | 1077 | { |
1060 | struct net_device *dev; | 1078 | struct net_device *dev; |
1079 | struct net_device *last; | ||
1061 | int err; | 1080 | int err; |
1062 | 1081 | ||
1063 | rtnl_lock(); | 1082 | rtnl_lock(); |
1064 | err = raw_notifier_chain_register(&netdev_chain, nb); | 1083 | err = raw_notifier_chain_register(&netdev_chain, nb); |
1065 | if (!err) { | 1084 | if (err) |
1066 | for_each_netdev(dev) { | 1085 | goto unlock; |
1067 | nb->notifier_call(nb, NETDEV_REGISTER, dev); | ||
1068 | 1086 | ||
1069 | if (dev->flags & IFF_UP) | 1087 | for_each_netdev(dev) { |
1070 | nb->notifier_call(nb, NETDEV_UP, dev); | 1088 | err = nb->notifier_call(nb, NETDEV_REGISTER, dev); |
1071 | } | 1089 | err = notifier_to_errno(err); |
1090 | if (err) | ||
1091 | goto rollback; | ||
1092 | |||
1093 | if (!(dev->flags & IFF_UP)) | ||
1094 | continue; | ||
1095 | |||
1096 | nb->notifier_call(nb, NETDEV_UP, dev); | ||
1072 | } | 1097 | } |
1098 | |||
1099 | unlock: | ||
1073 | rtnl_unlock(); | 1100 | rtnl_unlock(); |
1074 | return err; | 1101 | return err; |
1102 | |||
1103 | rollback: | ||
1104 | last = dev; | ||
1105 | for_each_netdev(dev) { | ||
1106 | if (dev == last) | ||
1107 | break; | ||
1108 | |||
1109 | if (dev->flags & IFF_UP) { | ||
1110 | nb->notifier_call(nb, NETDEV_GOING_DOWN, dev); | ||
1111 | nb->notifier_call(nb, NETDEV_DOWN, dev); | ||
1112 | } | ||
1113 | nb->notifier_call(nb, NETDEV_UNREGISTER, dev); | ||
1114 | } | ||
1115 | goto unlock; | ||
1075 | } | 1116 | } |
1076 | 1117 | ||
1077 | /** | 1118 | /** |
@@ -3434,9 +3475,10 @@ int register_netdevice(struct net_device *dev) | |||
3434 | write_unlock_bh(&dev_base_lock); | 3475 | write_unlock_bh(&dev_base_lock); |
3435 | 3476 | ||
3436 | /* Notify protocols, that a new device appeared. */ | 3477 | /* Notify protocols, that a new device appeared. */ |
3437 | raw_notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev); | 3478 | ret = raw_notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev); |
3438 | 3479 | ret = notifier_to_errno(ret); | |
3439 | ret = 0; | 3480 | if (ret) |
3481 | unregister_netdevice(dev); | ||
3440 | 3482 | ||
3441 | out: | 3483 | out: |
3442 | return ret; | 3484 | return ret; |