]> pilppa.org Git - linux-2.6-omap-h63xx.git/blobdiff - drivers/base/core.c
[TCP] FRTO: Improve interoperability with other undo_marker users
[linux-2.6-omap-h63xx.git] / drivers / base / core.c
index 0455aa78fa135cb04529efb281386d25423b2970..ec86d6fc2360bbb3fa6286a1972a7f82f36c3699 100644 (file)
@@ -284,6 +284,7 @@ static ssize_t show_uevent(struct device *dev, struct device_attribute *attr,
 
        /* let the kset specific function add its keys */
        pos = data;
+       memset(envp, 0, sizeof(envp));
        retval = kset->uevent_ops->uevent(kset, &dev->kobj,
                                          envp, ARRAY_SIZE(envp),
                                          pos, PAGE_SIZE);
@@ -303,10 +304,25 @@ out:
 static ssize_t store_uevent(struct device *dev, struct device_attribute *attr,
                            const char *buf, size_t count)
 {
-       if (memcmp(buf, "add", 3) != 0)
-               dev_err(dev, "uevent: unsupported action-string; this will "
-                       "be ignored in a future kernel version");
+       size_t len = count;
+       enum kobject_action action;
+
+       if (len && buf[len-1] == '\n')
+               len--;
+
+       for (action = 0; action < KOBJ_MAX; action++) {
+               if (strncmp(kobject_actions[action], buf, len) != 0)
+                       continue;
+               if (kobject_actions[action][len] != '\0')
+                       continue;
+               kobject_uevent(&dev->kobj, action);
+               goto out;
+       }
+
+       dev_err(dev, "uevent: unsupported action-string; this will "
+                    "be ignored in a future kernel version\n");
        kobject_uevent(&dev->kobj, KOBJ_ADD);
+out:
        return count;
 }
 
@@ -570,9 +586,13 @@ void device_initialize(struct device *dev)
 static struct kobject * get_device_parent(struct device *dev,
                                          struct device *parent)
 {
-       /* Set the parent to the class, not the parent device */
-       /* this keeps sysfs from having a symlink to make old udevs happy */
-       if (dev->class)
+       /*
+        * Set the parent to the class, not the parent device
+        * for topmost devices in class hierarchy.
+        * This keeps sysfs from having a symlink to make old
+        * udevs happy
+        */
+       if (dev->class && (!parent || parent->class != dev->class))
                return &dev->class->subsys.kobj;
        else if (parent)
                return &parent->kobj;
@@ -643,6 +663,98 @@ static int setup_parent(struct device *dev, struct device *parent)
        return 0;
 }
 
+static int device_add_class_symlinks(struct device *dev)
+{
+       int error;
+
+       if (!dev->class)
+               return 0;
+       error = sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj,
+                                 "subsystem");
+       if (error)
+               goto out;
+       /*
+        * If this is not a "fake" compatible device, then create the
+        * symlink from the class to the device.
+        */
+       if (dev->kobj.parent != &dev->class->subsys.kobj) {
+               error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj,
+                                         dev->bus_id);
+               if (error)
+                       goto out_subsys;
+       }
+       if (dev->parent) {
+#ifdef CONFIG_SYSFS_DEPRECATED
+               {
+                       struct device *parent = dev->parent;
+                       char *class_name;
+
+                       /*
+                        * In old sysfs stacked class devices had 'device'
+                        * link pointing to real device instead of parent
+                        */
+                       while (parent->class && !parent->bus && parent->parent)
+                               parent = parent->parent;
+
+                       error = sysfs_create_link(&dev->kobj,
+                                                 &parent->kobj,
+                                                 "device");
+                       if (error)
+                               goto out_busid;
+
+                       class_name = make_class_name(dev->class->name,
+                                                       &dev->kobj);
+                       if (class_name)
+                               error = sysfs_create_link(&dev->parent->kobj,
+                                                       &dev->kobj, class_name);
+                       kfree(class_name);
+                       if (error)
+                               goto out_device;
+               }
+#else
+               error = sysfs_create_link(&dev->kobj, &dev->parent->kobj,
+                                         "device");
+               if (error)
+                       goto out_busid;
+#endif
+       }
+       return 0;
+
+#ifdef CONFIG_SYSFS_DEPRECATED
+out_device:
+       if (dev->parent)
+               sysfs_remove_link(&dev->kobj, "device");
+#endif
+out_busid:
+       if (dev->kobj.parent != &dev->class->subsys.kobj)
+               sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+out_subsys:
+       sysfs_remove_link(&dev->kobj, "subsystem");
+out:
+       return error;
+}
+
+static void device_remove_class_symlinks(struct device *dev)
+{
+       if (!dev->class)
+               return;
+       if (dev->parent) {
+#ifdef CONFIG_SYSFS_DEPRECATED
+               char *class_name;
+
+               class_name = make_class_name(dev->class->name, &dev->kobj);
+               if (class_name) {
+                       sysfs_remove_link(&dev->parent->kobj, class_name);
+                       kfree(class_name);
+               }
+#endif
+               sysfs_remove_link(&dev->kobj, "device");
+       }
+       if (dev->kobj.parent != &dev->class->subsys.kobj)
+               sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id);
+       sysfs_remove_link(&dev->kobj, "subsystem");
+}
+
 /**
  *     device_add - add device to device hierarchy.
  *     @dev:   device.
@@ -657,7 +769,6 @@ static int setup_parent(struct device *dev, struct device *parent)
 int device_add(struct device *dev)
 {
        struct device *parent = NULL;
-       char *class_name = NULL;
        struct class_interface *class_intf;
        int error = -EINVAL;
 
@@ -697,27 +808,9 @@ int device_add(struct device *dev)
                        goto ueventattrError;
        }
 
-       if (dev->class) {
-               sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj,
-                                 "subsystem");
-               /* If this is not a "fake" compatible device, then create the
-                * symlink from the class to the device. */
-               if (dev->kobj.parent != &dev->class->subsys.kobj)
-                       sysfs_create_link(&dev->class->subsys.kobj,
-                                         &dev->kobj, dev->bus_id);
-               if (parent) {
-                       sysfs_create_link(&dev->kobj, &dev->parent->kobj,
-                                                       "device");
-#ifdef CONFIG_SYSFS_DEPRECATED
-                       class_name = make_class_name(dev->class->name,
-                                                       &dev->kobj);
-                       if (class_name)
-                               sysfs_create_link(&dev->parent->kobj,
-                                                 &dev->kobj, class_name);
-#endif
-               }
-       }
-
+       error = device_add_class_symlinks(dev);
+       if (error)
+               goto SymlinkError;
        error = device_add_attrs(dev);
        if (error)
                goto AttrsError;
@@ -744,7 +837,6 @@ int device_add(struct device *dev)
                up(&dev->class->sem);
        }
  Done:
-       kfree(class_name);
        put_device(dev);
        return error;
  BusError:
@@ -755,6 +847,8 @@ int device_add(struct device *dev)
                                             BUS_NOTIFY_DEL_DEVICE, dev);
        device_remove_attrs(dev);
  AttrsError:
+       device_remove_class_symlinks(dev);
+ SymlinkError:
        if (MAJOR(dev->devt))
                device_remove_file(dev, &devt_attr);
 
@@ -1139,7 +1233,7 @@ int device_rename(struct device *dev, char *new_name)
 {
        char *old_class_name = NULL;
        char *new_class_name = NULL;
-       char *old_symlink_name = NULL;
+       char *old_device_name = NULL;
        int error;
 
        dev = get_device(dev);
@@ -1153,42 +1247,49 @@ int device_rename(struct device *dev, char *new_name)
                old_class_name = make_class_name(dev->class->name, &dev->kobj);
 #endif
 
-       if (dev->class) {
-               old_symlink_name = kmalloc(BUS_ID_SIZE, GFP_KERNEL);
-               if (!old_symlink_name) {
-                       error = -ENOMEM;
-                       goto out_free_old_class;
-               }
-               strlcpy(old_symlink_name, dev->bus_id, BUS_ID_SIZE);
+       old_device_name = kmalloc(BUS_ID_SIZE, GFP_KERNEL);
+       if (!old_device_name) {
+               error = -ENOMEM;
+               goto out;
        }
-
+       strlcpy(old_device_name, dev->bus_id, BUS_ID_SIZE);
        strlcpy(dev->bus_id, new_name, BUS_ID_SIZE);
 
        error = kobject_rename(&dev->kobj, new_name);
+       if (error) {
+               strlcpy(dev->bus_id, old_device_name, BUS_ID_SIZE);
+               goto out;
+       }
 
 #ifdef CONFIG_SYSFS_DEPRECATED
        if (old_class_name) {
                new_class_name = make_class_name(dev->class->name, &dev->kobj);
                if (new_class_name) {
-                       sysfs_create_link(&dev->parent->kobj, &dev->kobj,
-                                         new_class_name);
+                       error = sysfs_create_link(&dev->parent->kobj,
+                                                 &dev->kobj, new_class_name);
+                       if (error)
+                               goto out;
                        sysfs_remove_link(&dev->parent->kobj, old_class_name);
                }
        }
 #endif
 
        if (dev->class) {
-               sysfs_remove_link(&dev->class->subsys.kobj,
-                                 old_symlink_name);
-               sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj,
-                                 dev->bus_id);
+               sysfs_remove_link(&dev->class->subsys.kobj, old_device_name);
+               error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj,
+                                         dev->bus_id);
+               if (error) {
+                       /* Uh... how to unravel this if restoring can fail? */
+                       dev_err(dev, "%s: sysfs_create_symlink failed (%d)\n",
+                               __FUNCTION__, error);
+               }
        }
+out:
        put_device(dev);
 
        kfree(new_class_name);
-       kfree(old_symlink_name);
- out_free_old_class:
        kfree(old_class_name);
+       kfree(old_device_name);
 
        return error;
 }