UIO: fix kobject usage
Greg Kroah-Hartman [Tue, 4 Dec 2007 22:41:54 +0000 (22:41 +0000)]
The uio kobject code is "wierd".  This patch should hopefully fix it up
to be sane and not leak memory anymore.

Cc: Kay Sievers <kay.sievers@vrfy.org>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Benedikt Spranger <b.spranger@linutronix.de>
Signed-off-by: Hans J. Koch <hjk@linutronix.de>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>

drivers/uio/uio.c
include/linux/uio_driver.h

index 606aae7..acc387d 100644 (file)
@@ -34,7 +34,7 @@ struct uio_device {
        wait_queue_head_t       wait;
        int                     vma_count;
        struct uio_info         *info;
-       struct kset             map_attr_kset;
+       struct kobject          *map_dir;
 };
 
 static int uio_major;
@@ -51,47 +51,48 @@ static struct uio_class {
  * attributes
  */
 
-static struct attribute attr_addr = {
-       .name  = "addr",
-       .mode  = S_IRUGO,
+struct uio_map {
+       struct kobject kobj;
+       struct uio_mem *mem;
 };
+#define to_map(map) container_of(map, struct uio_map, kobj)
 
-static struct attribute attr_size = {
-       .name  = "size",
-       .mode  = S_IRUGO,
-};
 
-static struct attribute* map_attrs[] = {
-       &attr_addr, &attr_size, NULL
-};
-
-static ssize_t map_attr_show(struct kobject *kobj, struct attribute *attr,
+static ssize_t map_attr_show(struct kobject *kobj, struct kobj_attribute *attr,
                             char *buf)
 {
-       struct uio_mem *mem = container_of(kobj, struct uio_mem, kobj);
+       struct uio_map *map = to_map(kobj);
+       struct uio_mem *mem = map->mem;
 
-       if (strncmp(attr->name,"addr",4) == 0)
+       if (strncmp(attr->attr.name, "addr", 4) == 0)
                return sprintf(buf, "0x%lx\n", mem->addr);
 
-       if (strncmp(attr->name,"size",4) == 0)
+       if (strncmp(attr->attr.name, "size", 4) == 0)
                return sprintf(buf, "0x%lx\n", mem->size);
 
        return -ENODEV;
 }
 
-static void map_attr_release(struct kobject *kobj)
-{
-       /* TODO ??? */
-}
+static struct kobj_attribute attr_attribute =
+       __ATTR(addr, S_IRUGO, map_attr_show, NULL);
+static struct kobj_attribute size_attribute =
+       __ATTR(size, S_IRUGO, map_attr_show, NULL);
 
-static struct sysfs_ops map_attr_ops = {
-       .show  = map_attr_show,
+static struct attribute *attrs[] = {
+       &attr_attribute.attr,
+       &size_attribute.attr,
+       NULL,   /* need to NULL terminate the list of attributes */
 };
 
+static void map_release(struct kobject *kobj)
+{
+       struct uio_map *map = to_map(kobj);
+       kfree(map);
+}
+
 static struct kobj_type map_attr_type = {
-       .release        = map_attr_release,
-       .sysfs_ops      = &map_attr_ops,
-       .default_attrs  = map_attrs,
+       .release        = map_release,
+       .default_attrs  = attrs,
 };
 
 static ssize_t show_name(struct device *dev,
@@ -148,6 +149,7 @@ static int uio_dev_add_attributes(struct uio_device *idev)
        int mi;
        int map_found = 0;
        struct uio_mem *mem;
+       struct uio_map *map;
 
        ret = sysfs_create_group(&idev->dev->kobj, &uio_attr_grp);
        if (ret)
@@ -159,31 +161,34 @@ static int uio_dev_add_attributes(struct uio_device *idev)
                        break;
                if (!map_found) {
                        map_found = 1;
-                       kobject_set_name(&idev->map_attr_kset.kobj,"maps");
-                       idev->map_attr_kset.kobj.ktype = &map_attr_type;
-                       idev->map_attr_kset.kobj.parent = &idev->dev->kobj;
-                       ret = kset_register(&idev->map_attr_kset);
-                       if (ret)
-                               goto err_remove_group;
+                       idev->map_dir = kobject_create_and_add("maps",
+                                                       &idev->dev->kobj);
+                       if (!idev->map_dir)
+                               goto err;
                }
-               kobject_init(&mem->kobj);
-               kobject_set_name(&mem->kobj,"map%d",mi);
-               mem->kobj.parent = &idev->map_attr_kset.kobj;
-               mem->kobj.kset = &idev->map_attr_kset;
-               ret = kobject_add(&mem->kobj);
+               map = kzalloc(sizeof(*map), GFP_KERNEL);
+               if (!map)
+                       goto err;
+               kobject_init_ng(&map->kobj, &map_attr_type);
+               map->mem = mem;
+               mem->map = map;
+               ret = kobject_add_ng(&map->kobj, idev->map_dir, "map%d", mi);
+               if (ret)
+                       goto err;
+               ret = kobject_uevent(&map->kobj, KOBJ_ADD);
                if (ret)
-                       goto err_remove_maps;
+                       goto err;
        }
 
        return 0;
 
-err_remove_maps:
+err:
        for (mi--; mi>=0; mi--) {
                mem = &idev->info->mem[mi];
-               kobject_unregister(&mem->kobj);
+               map = mem->map;
+               kobject_unregister(&map->kobj);
        }
-       kset_unregister(&idev->map_attr_kset); /* Needed ? */
-err_remove_group:
+       kobject_unregister(idev->map_dir);
        sysfs_remove_group(&idev->dev->kobj, &uio_attr_grp);
 err_group:
        dev_err(idev->dev, "error creating sysfs files (%d)\n", ret);
@@ -198,9 +203,9 @@ static void uio_dev_del_attributes(struct uio_device *idev)
                mem = &idev->info->mem[mi];
                if (mem->size == 0)
                        break;
-               kobject_unregister(&mem->kobj);
+               kobject_unregister(&mem->map->kobj);
        }
-       kset_unregister(&idev->map_attr_kset);
+       kobject_unregister(idev->map_dir);
        sysfs_remove_group(&idev->dev->kobj, &uio_attr_grp);
 }
 
index 44c28e9..973386d 100644 (file)
 #include <linux/fs.h>
 #include <linux/interrupt.h>
 
+struct uio_map;
+
 /**
  * struct uio_mem - description of a UIO memory region
- * @kobj:              kobject for this mapping
  * @addr:              address of the device's memory
  * @size:              size of IO
  * @memtype:           type of memory addr points to
  * @internal_addr:     ioremap-ped version of addr, for driver internal use
+ * @map:               for use by the UIO core only.
  */
 struct uio_mem {
-       struct kobject          kobj;
        unsigned long           addr;
        unsigned long           size;
        int                     memtype;
        void __iomem            *internal_addr;
+       struct uio_map          *map;
 };
 
 #define MAX_UIO_MAPS   5