From 1f1214d28527ffbec3424e433c96f982b8668d67 Mon Sep 17 00:00:00 2001 From: Herbert Xu Date: Mon, 30 Jul 2007 17:03:38 -0700 Subject: [PATCH] --- yaml --- r: 63405 b: refs/heads/master c: fcc5a03ac42564e9e255c1134dda47442289e466 h: refs/heads/master i: 63403: 106299b7000293fd1217580adaba6604b4a57e2f v: v3 --- [refs] | 2 +- trunk/include/linux/notifier.h | 13 +++++++ trunk/net/core/dev.c | 62 ++++++++++++++++++++++++++++------ 3 files changed, 66 insertions(+), 11 deletions(-) diff --git a/[refs] b/[refs] index dbd5e04b14ba..d215e081bc09 100644 --- a/[refs] +++ b/[refs] @@ -1,2 +1,2 @@ --- -refs/heads/master: aeed9e82cd258b9699eaa6568efefba9cc6d5f01 +refs/heads/master: fcc5a03ac42564e9e255c1134dda47442289e466 diff --git a/trunk/include/linux/notifier.h b/trunk/include/linux/notifier.h index be3f2bb6fcf3..fad7ff17e468 100644 --- a/trunk/include/linux/notifier.h +++ b/trunk/include/linux/notifier.h @@ -157,6 +157,19 @@ extern int __srcu_notifier_call_chain(struct srcu_notifier_head *nh, */ #define NOTIFY_STOP (NOTIFY_OK|NOTIFY_STOP_MASK) +/* Encapsulate (negative) errno value (in particular, NOTIFY_BAD <=> EPERM). */ +static inline int notifier_from_errno(int err) +{ + return NOTIFY_STOP_MASK | (NOTIFY_OK - err); +} + +/* Restore (negative) errno value from notify return value. */ +static inline int notifier_to_errno(int ret) +{ + ret &= ~NOTIFY_STOP_MASK; + return ret > NOTIFY_OK ? NOTIFY_OK - ret : 0; +} + /* * Declared notifiers so far. I can imagine quite a few more chains * over time (eg laptop power reset chains, reboot chain (to clean diff --git a/trunk/net/core/dev.c b/trunk/net/core/dev.c index 346cbf66534e..6cc8a70350ac 100644 --- a/trunk/net/core/dev.c +++ b/trunk/net/core/dev.c @@ -817,7 +817,9 @@ int dev_alloc_name(struct net_device *dev, const char *name) */ int dev_change_name(struct net_device *dev, char *newname) { + char oldname[IFNAMSIZ]; int err = 0; + int ret; ASSERT_RTNL(); @@ -827,6 +829,8 @@ int dev_change_name(struct net_device *dev, char *newname) if (!dev_valid_name(newname)) return -EINVAL; + memcpy(oldname, dev->name, IFNAMSIZ); + if (strchr(newname, '%')) { err = dev_alloc_name(dev, newname); if (err < 0) @@ -838,6 +842,7 @@ int dev_change_name(struct net_device *dev, char *newname) else strlcpy(dev->name, newname, IFNAMSIZ); +rollback: device_rename(&dev->dev, dev->name); write_lock_bh(&dev_base_lock); @@ -845,7 +850,20 @@ int dev_change_name(struct net_device *dev, char *newname) hlist_add_head(&dev->name_hlist, dev_name_hash(dev->name)); write_unlock_bh(&dev_base_lock); - raw_notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev); + ret = raw_notifier_call_chain(&netdev_chain, NETDEV_CHANGENAME, dev); + ret = notifier_to_errno(ret); + + if (ret) { + if (err) { + printk(KERN_ERR + "%s: name change rollback failed: %d.\n", + dev->name, ret); + } else { + err = ret; + memcpy(dev->name, oldname, IFNAMSIZ); + goto rollback; + } + } return err; } @@ -1058,20 +1076,43 @@ int dev_close(struct net_device *dev) int register_netdevice_notifier(struct notifier_block *nb) { struct net_device *dev; + struct net_device *last; int err; rtnl_lock(); err = raw_notifier_chain_register(&netdev_chain, nb); - if (!err) { - for_each_netdev(dev) { - nb->notifier_call(nb, NETDEV_REGISTER, dev); + if (err) + goto unlock; - if (dev->flags & IFF_UP) - nb->notifier_call(nb, NETDEV_UP, dev); - } + for_each_netdev(dev) { + err = nb->notifier_call(nb, NETDEV_REGISTER, dev); + err = notifier_to_errno(err); + if (err) + goto rollback; + + if (!(dev->flags & IFF_UP)) + continue; + + nb->notifier_call(nb, NETDEV_UP, dev); } + +unlock: rtnl_unlock(); return err; + +rollback: + last = dev; + for_each_netdev(dev) { + if (dev == last) + break; + + if (dev->flags & IFF_UP) { + nb->notifier_call(nb, NETDEV_GOING_DOWN, dev); + nb->notifier_call(nb, NETDEV_DOWN, dev); + } + nb->notifier_call(nb, NETDEV_UNREGISTER, dev); + } + goto unlock; } /** @@ -3434,9 +3475,10 @@ int register_netdevice(struct net_device *dev) write_unlock_bh(&dev_base_lock); /* Notify protocols, that a new device appeared. */ - raw_notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev); - - ret = 0; + ret = raw_notifier_call_chain(&netdev_chain, NETDEV_REGISTER, dev); + ret = notifier_to_errno(ret); + if (ret) + unregister_netdevice(dev); out: return ret;