bridge: update sysfs link names if port device names have changed
authorSimon Arlott <simon@fire.lp0.eu>
Mon, 10 May 2010 09:31:11 +0000 (09:31 +0000)
committerDavid S. Miller <davem@davemloft.net>
Sun, 16 May 2010 06:10:15 +0000 (23:10 -0700)
Links for each port are created in sysfs using the device
name, but this could be changed after being added to the
bridge.

As well as being unable to remove interfaces after this
occurs (because userspace tools don't recognise the new
name, and the kernel won't recognise the old name), adding
another interface with the old name to the bridge will
cause an error trying to create the sysfs link.

This fixes the problem by listening for NETDEV_CHANGENAME
notifications and renaming the link.

https://bugzilla.kernel.org/show_bug.cgi?id=12743

Signed-off-by: Simon Arlott <simon@fire.lp0.eu>
Acked-by: Stephen Hemminger <shemminger@vyatta.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
fs/sysfs/symlink.c
net/bridge/br_if.c
net/bridge/br_notify.c
net/bridge/br_private.h
net/bridge/br_sysfs_if.c

index b93ec51fa7ace3ee4a9931512761ba7dcf0207ed..942f239a2132c8b12d47c1258bb02cfbd17a2726 100644 (file)
@@ -261,3 +261,4 @@ const struct inode_operations sysfs_symlink_inode_operations = {
 
 EXPORT_SYMBOL_GPL(sysfs_create_link);
 EXPORT_SYMBOL_GPL(sysfs_remove_link);
+EXPORT_SYMBOL_GPL(sysfs_rename_link);
index 45f3f8871149cbd2af0a81552b378f81d49128f2..f25e3c92bd72afb128cdae74af98daa93f08a8f5 100644 (file)
@@ -133,7 +133,7 @@ static void del_nbp(struct net_bridge_port *p)
        struct net_bridge *br = p->br;
        struct net_device *dev = p->dev;
 
-       sysfs_remove_link(br->ifobj, dev->name);
+       sysfs_remove_link(br->ifobj, p->sysfs_name);
 
        dev_set_promiscuity(dev, -1);
 
index 1413b72acc7fd8851fa163423582665f4f097fb0..717e1fd6133cbfcd3815e7f13d912597524faa9f 100644 (file)
@@ -34,6 +34,7 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
        struct net_device *dev = ptr;
        struct net_bridge_port *p = dev->br_port;
        struct net_bridge *br;
+       int err;
 
        /* not a port of a bridge */
        if (p == NULL)
@@ -83,6 +84,12 @@ static int br_device_event(struct notifier_block *unused, unsigned long event, v
                br_del_if(br, dev);
                break;
 
+       case NETDEV_CHANGENAME:
+               err = br_sysfs_renameif(p);
+               if (err)
+                       return notifier_from_errno(err);
+               break;
+
        case NETDEV_PRE_TYPE_CHANGE:
                /* Forbid underlaying device to change its type. */
                return NOTIFY_BAD;
index 79a13d1c25735016a3f388bc316889984b003852..0f4a74bc6a9b58e06b34194117577d7817b8be01 100644 (file)
@@ -139,6 +139,10 @@ struct net_bridge_port
        struct hlist_head               mglist;
        struct hlist_node               rlist;
 #endif
+
+#ifdef CONFIG_SYSFS
+       char                            sysfs_name[IFNAMSIZ];
+#endif
 };
 
 struct br_cpu_netstats {
@@ -480,6 +484,7 @@ extern void br_ifinfo_notify(int event, struct net_bridge_port *port);
 /* br_sysfs_if.c */
 extern const struct sysfs_ops brport_sysfs_ops;
 extern int br_sysfs_addif(struct net_bridge_port *p);
+extern int br_sysfs_renameif(struct net_bridge_port *p);
 
 /* br_sysfs_br.c */
 extern int br_sysfs_addbr(struct net_device *dev);
@@ -488,6 +493,7 @@ extern void br_sysfs_delbr(struct net_device *dev);
 #else
 
 #define br_sysfs_addif(p)      (0)
+#define br_sysfs_renameif(p)   (0)
 #define br_sysfs_addbr(dev)    (0)
 #define br_sysfs_delbr(dev)    do { } while(0)
 #endif /* CONFIG_SYSFS */
index 0b9916489d6bf0c1eb61ec2048ddeb0472221923..fd5799c9bc8de350df12db5e35331990cc46eb03 100644 (file)
@@ -246,7 +246,7 @@ const struct sysfs_ops brport_sysfs_ops = {
 /*
  * Add sysfs entries to ethernet device added to a bridge.
  * Creates a brport subdirectory with bridge attributes.
- * Puts symlink in bridge's brport subdirectory
+ * Puts symlink in bridge's brif subdirectory
  */
 int br_sysfs_addif(struct net_bridge_port *p)
 {
@@ -257,15 +257,37 @@ int br_sysfs_addif(struct net_bridge_port *p)
        err = sysfs_create_link(&p->kobj, &br->dev->dev.kobj,
                                SYSFS_BRIDGE_PORT_LINK);
        if (err)
-               goto out2;
+               return err;
 
        for (a = brport_attrs; *a; ++a) {
                err = sysfs_create_file(&p->kobj, &((*a)->attr));
                if (err)
-                       goto out2;
+                       return err;
        }
 
-       err = sysfs_create_link(br->ifobj, &p->kobj, p->dev->name);
-out2:
+       strlcpy(p->sysfs_name, p->dev->name, IFNAMSIZ);
+       return sysfs_create_link(br->ifobj, &p->kobj, p->sysfs_name);
+}
+
+/* Rename bridge's brif symlink */
+int br_sysfs_renameif(struct net_bridge_port *p)
+{
+       struct net_bridge *br = p->br;
+       int err;
+
+       /* If a rename fails, the rollback will cause another
+        * rename call with the existing name.
+        */
+       if (!strncmp(p->sysfs_name, p->dev->name, IFNAMSIZ))
+               return 0;
+
+       err = sysfs_rename_link(br->ifobj, &p->kobj,
+                               p->sysfs_name, p->dev->name);
+       if (err)
+               netdev_notice(br->dev, "unable to rename link %s to %s",
+                             p->sysfs_name, p->dev->name);
+       else
+               strlcpy(p->sysfs_name, p->dev->name, IFNAMSIZ);
+
        return err;
 }