blob: 787f03de811bf96c42d684a85d2a4b570c2f9e41 [file] [log] [blame]
>From kay.sievers@novell.com Sat Oct 7 13:11:38 2006
From: Kay Sievers <kay.sievers@novell.com>
Date: Sat, 07 Oct 2006 21:54:55 +0200
Subject: Driver core: unify /sys/class and /sys/bus at /sys/subsystem
Message-Id: <1160250895.4235.24.camel@localhost>
This merges the classification information from both device types,
class devices and bus devices into a single userspace export. It was
a design mistake in the first place and the kernel should stop
exporting such private implementation details to userspace.
The /sys/class and /sys/bus directories are still created with their
individual content but should no longer be used from userspace.
/sys/subsystems has the same layout structure as /sys/bus and
contains all devices of a subsystem in a "devices" directory.
It properly exports subsystem specific attributes without creating
possible conflicts with device names, something the flat class
directory layout can't do.
Subsystems from a bus and a class of the same name merge into
a single instance.
Signed-off-by: Kay Sievers <kay.sievers@novell.com>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
---
drivers/base/base.h | 4 -
drivers/base/bus.c | 39 ++++++++------
drivers/base/class.c | 133 ++++++++++++++++++++++++++++++------------------
drivers/base/core.c | 78 ++++++++++++++++------------
drivers/base/sys.c | 2
drivers/usb/core/file.c | 62 ++--------------------
drivers/usb/core/usb.c | 6 ++
drivers/usb/core/usb.h | 1
include/linux/device.h | 8 ++
9 files changed, 175 insertions(+), 158 deletions(-)
--- a/drivers/base/base.h
+++ b/drivers/base/base.h
@@ -1,5 +1,5 @@
-
-/* initialisation functions */
+extern struct subsystem devices_subsys;
+extern struct subsystem subs_subsys;
extern int devices_init(void);
extern int buses_init(void);
--- a/drivers/base/bus.c
+++ b/drivers/base/bus.c
@@ -18,14 +18,13 @@
#define to_bus_attr(_attr) container_of(_attr, struct bus_attribute, attr)
#define to_bus(obj) container_of(obj, struct bus_type, subsys.kset.kobj)
-
-/*
- * sysfs bindings for drivers
- */
-
#define to_drv_attr(_attr) container_of(_attr, struct driver_attribute, attr)
#define to_driver(obj) container_of(obj, struct device_driver, kobj)
+/* /sys/bus for compatibility, real location is /sys/subsystems */
+#ifdef CONFIG_SYSFS_DEPRECATED_FUTURE
+static struct kobject bus_depr;
+#endif
static int __must_check bus_rescan_devices_helper(struct device *dev,
void *data);
@@ -119,6 +118,10 @@ static struct sysfs_ops bus_sysfs_ops =
.store = bus_attr_store,
};
+static struct kobj_type bus_ktype = {
+ .sysfs_ops = &bus_sysfs_ops,
+};
+
int bus_create_file(struct bus_type * bus, struct bus_attribute * attr)
{
int error;
@@ -138,14 +141,6 @@ void bus_remove_file(struct bus_type * b
}
}
-static struct kobj_type ktype_bus = {
- .sysfs_ops = &bus_sysfs_ops,
-
-};
-
-static decl_subsys(bus, &ktype_bus, NULL);
-
-
#ifdef CONFIG_HOTPLUG
/* Manually detach a device from its associated driver. */
static int driver_helper(struct device *dev, void *data)
@@ -812,7 +807,8 @@ int bus_register(struct bus_type * bus)
if (retval)
goto out;
- subsys_set_kset(bus, bus_subsys);
+ subsys_set_kset(bus, subs_subsys);
+ bus->subsys.kset.kobj.ktype = &bus_ktype;
retval = subsystem_register(&bus->subsys);
if (retval)
goto out;
@@ -842,6 +838,10 @@ int bus_register(struct bus_type * bus)
if (retval)
goto bus_attrs_fail;
+#ifdef CONFIG_SYSFS_DEPRECATED_FUTURE
+ sysfs_create_link(&bus_depr, &bus->subsys.kset.kobj, bus->name);
+#endif
+
pr_debug("bus type '%s' registered\n", bus->name);
return 0;
@@ -867,6 +867,9 @@ out:
void bus_unregister(struct bus_type * bus)
{
pr_debug("bus %s: unregistering\n", bus->name);
+#ifdef CONFIG_SYSFS_DEPRECATED_FUTURE
+ sysfs_remove_link(&bus_depr, bus->name);
+#endif
bus_remove_attrs(bus);
remove_probe_files(bus);
kset_unregister(&bus->drivers);
@@ -888,10 +891,14 @@ EXPORT_SYMBOL_GPL(bus_unregister_notifie
int __init buses_init(void)
{
- return subsystem_register(&bus_subsys);
+#ifdef CONFIG_SYSFS_DEPRECATED_FUTURE
+ kobject_init(&bus_depr);
+ kobject_set_name(&bus_depr, "bus");
+ return kobject_add(&bus_depr);
+#endif
+ return 0;
}
-
EXPORT_SYMBOL_GPL(bus_for_each_dev);
EXPORT_SYMBOL_GPL(bus_find_device);
EXPORT_SYMBOL_GPL(bus_for_each_drv);
--- a/drivers/base/class.c
+++ b/drivers/base/class.c
@@ -19,11 +19,17 @@
#include <linux/slab.h>
#include "base.h"
-extern struct subsystem devices_subsys;
#define to_class_attr(_attr) container_of(_attr, struct class_attribute, attr)
#define to_class(obj) container_of(obj, struct class, subsys.kset.kobj)
+static struct kset_uevent_ops class_dev_uevent_ops;
+
+#ifdef CONFIG_SYSFS_DEPRECATED_FUTURE
+/* /sys/class for compatibility, real location is /sys/subsystems */
+static struct kobject class_depr;
+#endif
+
static ssize_t
class_attr_show(struct kobject * kobj, struct attribute * attr, char * buf)
{
@@ -67,20 +73,19 @@ static struct sysfs_ops class_sysfs_ops
.store = class_attr_store,
};
-static struct kobj_type ktype_class = {
+static struct kobj_type class_ktype = {
.sysfs_ops = &class_sysfs_ops,
.release = class_release,
};
-/* Hotplug events for classes go to the class_obj subsys */
-static decl_subsys(class, &ktype_class, NULL);
-
-
int class_create_file(struct class * cls, const struct class_attribute * attr)
{
int error;
if (cls) {
error = sysfs_create_file(&cls->subsys.kset.kobj, &attr->attr);
+#ifdef CONFIG_SYSFS_DEPRECATED_FUTURE
+ error = sysfs_create_file(&cls->class_dir_depr, &attr->attr);
+#endif
} else
error = -EINVAL;
return error;
@@ -88,8 +93,12 @@ int class_create_file(struct class * cls
void class_remove_file(struct class * cls, const struct class_attribute * attr)
{
- if (cls)
+ if (cls) {
+#ifdef CONFIG_SYSFS_DEPRECATED_FUTURE
+ sysfs_remove_file(&cls->class_dir_depr, &attr->attr);
+#endif
sysfs_remove_file(&cls->subsys.kset.kobj, &attr->attr);
+ }
}
static struct class *class_get(struct class *cls)
@@ -149,23 +158,59 @@ int class_register(struct class * cls)
init_MUTEX(&cls->sem);
error = kobject_set_name(&cls->subsys.kset.kobj, "%s", cls->name);
if (error)
- return error;
+ goto out;
- subsys_set_kset(cls, class_subsys);
+ subsys_set_kset(cls, subs_subsys);
+ cls->subsys.kset.kobj.ktype = &class_ktype;
- error = subsystem_register(&cls->subsys);
- if (!error) {
+ /* if we join a bus's directory, don't register */
+ if (cls->bus) {
+ get_bus(cls->bus);
+ subsystem_init(&cls->subsys);
+ } else {
+ error = subsystem_register(&cls->subsys);
+ if (error)
+ goto out;
error = add_class_attrs(class_get(cls));
class_put(cls);
+ kobject_set_name(&cls->devices_dir.kobj, "devices");
+ cls->devices_dir.subsys = &cls->subsys;
+
+ /* old struct class_device's uevent handler */
+ cls->devices_dir.uevent_ops = &class_dev_uevent_ops;
+
+ error = kset_register(&cls->devices_dir);
+ if (error)
+ goto out;
}
+
+#ifdef CONFIG_SYSFS_DEPRECATED_FUTURE
+ kobject_init(&cls->class_dir_depr);
+ kobject_set_name(&cls->class_dir_depr, cls->name);
+ cls->class_dir_depr.parent = &class_depr;
+ error = kobject_add(&cls->class_dir_depr);
+#endif
+
+out:
return error;
}
void class_unregister(struct class * cls)
{
pr_debug("device class '%s': unregistering\n", cls->name);
- remove_class_attrs(cls);
- subsystem_unregister(&cls->subsys);
+
+#ifdef CONFIG_SYSFS_DEPRECATED_FUTURE
+ kobject_del(&cls->class_dir_depr);
+ kobject_put(&cls->class_dir_depr);
+#endif
+
+ if (cls->bus)
+ put_bus(cls->bus);
+ else {
+ remove_class_attrs(cls);
+ kset_unregister(&cls->devices_dir);
+ subsystem_unregister(&cls->subsys);
+ }
}
static void class_create_release(struct class *cls)
@@ -180,15 +225,6 @@ static void class_device_create_release(
kfree(class_dev);
}
-/* needed to allow these devices to have parent class devices */
-static int class_device_create_uevent(struct class_device *class_dev,
- char **envp, int num_envp,
- char *buffer, int buffer_size)
-{
- pr_debug("%s called for %s\n", __FUNCTION__, class_dev->class_id);
- return 0;
-}
-
/**
* class_create - create a struct class structure
* @owner: pointer to the module that is to "own" this struct class
@@ -329,7 +365,7 @@ static void class_dev_release(struct kob
}
}
-static struct kobj_type ktype_class_device = {
+static struct kobj_type class_device_ktype = {
.sysfs_ops = &class_dev_sysfs_ops,
.release = class_dev_release,
};
@@ -338,7 +374,7 @@ static int class_uevent_filter(struct ks
{
struct kobj_type *ktype = get_ktype(kobj);
- if (ktype == &ktype_class_device) {
+ if (ktype == &class_device_ktype) {
struct class_device *class_dev = to_class_dev(kobj);
if (class_dev->class)
return 1;
@@ -473,13 +509,7 @@ static int class_uevent(struct kset *kse
buffer = &buffer[length];
buffer_size -= length;
- if (class_dev->uevent) {
- /* have the class device specific function add its stuff */
- retval = class_dev->uevent(class_dev, envp, num_envp,
- buffer, buffer_size);
- if (retval)
- pr_debug("class_dev->uevent() returned %d\n", retval);
- } else if (class_dev->class->uevent) {
+ if (class_dev->class->uevent) {
/* have the class specific function add its stuff */
retval = class_dev->class->uevent(class_dev, envp, num_envp,
buffer, buffer_size);
@@ -490,15 +520,12 @@ static int class_uevent(struct kset *kse
return retval;
}
-static struct kset_uevent_ops class_uevent_ops = {
+static struct kset_uevent_ops class_dev_uevent_ops = {
.filter = class_uevent_filter,
.name = class_uevent_name,
.uevent = class_uevent,
};
-static decl_subsys(class_obj, &ktype_class_device, &class_uevent_ops);
-
-
static int class_device_add_attrs(struct class_device * cd)
{
int i;
@@ -575,9 +602,9 @@ static ssize_t store_uevent(struct class
void class_device_initialize(struct class_device *class_dev)
{
- kobj_set_kset_s(class_dev, class_obj_subsys);
kobject_init(&class_dev->kobj);
INIT_LIST_HEAD(&class_dev->node);
+ class_dev->kobj.ktype = &class_device_ktype;
}
int class_device_add(struct class_device *class_dev)
@@ -608,10 +635,20 @@ int class_device_add(struct class_device
if (error)
goto out2;
+#ifdef CONFIG_SYSFS_DEPRECATED_FUTURE
+ sysfs_create_link(&parent_class->class_dir_depr,
+ &class_dev->kobj, class_dev->class_id);
+#endif
+
if (parent_class_dev)
+ /* stacked class devices are not handled by userspace, avoid it */
class_dev->kobj.parent = &parent_class_dev->kobj;
- else
- class_dev->kobj.parent = &parent_class->subsys.kset.kobj;
+ else {
+ /* assign parent kset for uevent hook */
+ class_dev->kobj.kset = kset_get(&parent_class->devices_dir);
+ /* the device directory in /sys/subsystem/<name>/devices */
+ class_dev->kobj.parent = &parent_class->devices_dir.kobj;
+ }
error = kobject_add(&class_dev->kobj);
if (error)
@@ -755,7 +792,6 @@ struct class_device *class_device_create
class_dev->class = cls;
class_dev->parent = parent;
class_dev->release = class_device_create_release;
- class_dev->uevent = class_device_create_uevent;
va_start(args, fmt);
vsnprintf(class_dev->class_id, BUS_ID_SIZE, fmt, args);
@@ -786,6 +822,10 @@ void class_device_del(struct class_devic
up(&parent_class->sem);
}
+#ifdef CONFIG_SYSFS_DEPRECATED_FUTURE
+ sysfs_remove_link(&parent_class->class_dir_depr, class_dev->class_id);
+#endif
+
if (class_dev->dev) {
remove_deprecated_class_device_links(class_dev);
sysfs_remove_link(&class_dev->kobj, "device");
@@ -906,17 +946,12 @@ void class_interface_unregister(struct c
int __init classes_init(void)
{
- int retval;
-
- retval = subsystem_register(&class_subsys);
- if (retval)
- return retval;
+#ifdef CONFIG_SYSFS_DEPRECATED_FUTURE
+ kobject_init(&class_depr);
+ kobject_set_name(&class_depr, "class");
+ return kobject_add(&class_depr);
+#endif
- /* ick, this is ugly, the things we go through to keep from showing up
- * in sysfs... */
- subsystem_init(&class_obj_subsys);
- if (!class_obj_subsys.kset.subsys)
- class_obj_subsys.kset.subsys = &class_obj_subsys;
return 0;
}
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -27,10 +27,6 @@
int (*platform_notify)(struct device * dev) = NULL;
int (*platform_notify_remove)(struct device * dev) = NULL;
-/*
- * sysfs bindings for devices.
- */
-
/**
* dev_driver_string - Return a device's driver name, if at all possible
* @dev: struct device to get the name of
@@ -213,6 +209,14 @@ static int dev_uevent(struct kset *kset,
buffer = &buffer[length];
buffer_size -= length;
+ if (dev->type && dev->type->uevent) {
+ /* have the device type specific fuction add its stuff */
+ retval = dev->type->uevent(dev, envp, num_envp, buffer, buffer_size);
+ if (retval)
+ pr_debug("%s: dev_type uevent() returned %d\n",
+ __FUNCTION__, retval);
+ }
+
if (dev->bus && dev->bus->uevent) {
/* have the bus specific function add its stuff */
retval = dev->bus->uevent(dev, envp, num_envp, buffer, buffer_size);
@@ -229,14 +233,6 @@ static int dev_uevent(struct kset *kset,
__FUNCTION__, retval);
}
- if (dev->type && dev->type->uevent) {
- /* have the device type specific fuction add its stuff */
- retval = dev->type->uevent(dev, envp, num_envp, buffer, buffer_size);
- if (retval)
- pr_debug("%s: dev_type uevent() returned %d\n",
- __FUNCTION__, retval);
- }
-
return retval;
}
@@ -416,13 +412,6 @@ static ssize_t show_dev(struct device *d
return print_dev_t(buf, dev->devt);
}
-/*
- * devices_subsys - structure to be registered with kobject core.
- */
-
-decl_subsys(devices, &ktype_device, &device_uevent_ops);
-
-
/**
* device_create_file - create sysfs attribute file for device.
* @dev: device.
@@ -698,13 +687,21 @@ int device_add(struct device *dev)
}
if (dev->class) {
- sysfs_create_link(&dev->kobj, &dev->class->subsys.kset.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.kset.kobj)
- sysfs_create_link(&dev->class->subsys.kset.kobj,
- &dev->kobj, dev->bus_id);
+ struct kobject *devices_dir;
+ struct kobject *subsys_dir;
+
+ if (dev->class->bus) {
+ devices_dir = &dev->class->bus->devices.kobj;
+ subsys_dir = &dev->class->bus->subsys.kset.kobj;
+ } else {
+ devices_dir = &dev->class->devices_dir.kobj;
+ subsys_dir = &dev->class->subsys.kset.kobj;
+ }
+ sysfs_create_link(&dev->kobj, subsys_dir, "subsystem");
+ sysfs_create_link(devices_dir, &dev->kobj, dev->bus_id);
+#ifdef CONFIG_SYSFS_DEPRECATED_FUTURE
+ sysfs_create_link(&dev->class->class_dir_depr, &dev->kobj, dev->bus_id);
+#endif
if (parent) {
sysfs_create_link(&dev->kobj, &dev->parent->kobj,
"device");
@@ -860,12 +857,18 @@ void device_del(struct device * dev)
kfree(dev->devt_attr);
}
if (dev->class) {
+ struct kobject *devices_dir;
+
+ if (dev->class->bus)
+ devices_dir = &dev->class->bus->devices.kobj;
+ else
+ devices_dir = &dev->class->devices_dir.kobj;
+
sysfs_remove_link(&dev->kobj, "subsystem");
- /* If this is not a "fake" compatible device, remove the
- * symlink from the class to the device. */
- if (dev->kobj.parent != &dev->class->subsys.kset.kobj)
- sysfs_remove_link(&dev->class->subsys.kset.kobj,
- dev->bus_id);
+ sysfs_remove_link(devices_dir, dev->bus_id);
+#ifdef CONFIG_SYSFS_DEPRECATED_FUTURE
+ sysfs_remove_link(&dev->class->class_dir_depr, dev->bus_id);
+#endif
if (parent && parent->bus) {
#ifdef CONFIG_SYSFS_DEPRECATED
char *class_name = make_class_name(dev->class->name,
@@ -1021,9 +1024,20 @@ struct device * device_find_child(struct
return child;
}
+/* /sys/devices - holds the device tree */
+decl_subsys(devices, &ktype_device, &device_uevent_ops);
+
+/* /sys/subsystem - holds all buses and classes of devices */
+decl_subsys_name(subs, subsystem, NULL, NULL);
+
int __init devices_init(void)
{
- return subsystem_register(&devices_subsys);
+ int retval;
+
+ retval = subsystem_register(&devices_subsys);
+ if (!retval);
+ retval = subsystem_register(&subs_subsys);
+ return retval;
}
EXPORT_SYMBOL_GPL(device_for_each_child);
--- a/drivers/base/sys.c
+++ b/drivers/base/sys.c
@@ -25,8 +25,6 @@
#include "base.h"
-extern struct subsystem devices_subsys;
-
#define to_sysdev(k) container_of(k, struct sys_device, kobj)
#define to_sysdev_attr(a) container_of(a, struct sysdev_attribute, attr)
--- a/drivers/usb/core/file.c
+++ b/drivers/usb/core/file.c
@@ -60,52 +60,10 @@ static const struct file_operations usb_
.open = usb_open,
};
-static struct usb_class {
- struct kref kref;
- struct class *class;
-} *usb_class;
-
-static int init_usb_class(void)
-{
- int result = 0;
-
- if (usb_class != NULL) {
- kref_get(&usb_class->kref);
- goto exit;
- }
-
- usb_class = kmalloc(sizeof(*usb_class), GFP_KERNEL);
- if (!usb_class) {
- result = -ENOMEM;
- goto exit;
- }
-
- kref_init(&usb_class->kref);
- usb_class->class = class_create(THIS_MODULE, "usb");
- if (IS_ERR(usb_class->class)) {
- result = IS_ERR(usb_class->class);
- err("class_create failed for usb devices");
- kfree(usb_class);
- usb_class = NULL;
- }
-
-exit:
- return result;
-}
-
-static void release_usb_class(struct kref *kref)
-{
- /* Ok, we cheat as we know we only have one usb_class */
- class_destroy(usb_class->class);
- kfree(usb_class);
- usb_class = NULL;
-}
-
-static void destroy_usb_class(void)
-{
- if (usb_class)
- kref_put(&usb_class->kref, release_usb_class);
-}
+struct class usb_class = {
+ .name = "usb",
+ .bus = &usb_bus_type,
+};
int usb_major_init(void)
{
@@ -181,20 +139,15 @@ int usb_register_dev(struct usb_interfac
if (retval)
goto exit;
- retval = init_usb_class();
- if (retval)
- goto exit;
-
- intf->minor = minor;
-
/* create a usb class device for this usb interface */
+ intf->minor = minor;
snprintf(name, BUS_ID_SIZE, class_driver->name, minor - minor_base);
temp = strrchr(name, '/');
if (temp && (temp[1] != 0x00))
++temp;
else
temp = name;
- intf->usb_dev = device_create(usb_class->class, &intf->dev,
+ intf->usb_dev = device_create(&usb_class, &intf->dev,
MKDEV(USB_MAJOR, minor), "%s", temp);
if (IS_ERR(intf->usb_dev)) {
spin_lock (&minor_lock);
@@ -241,10 +194,9 @@ void usb_deregister_dev(struct usb_inter
spin_unlock (&minor_lock);
snprintf(name, BUS_ID_SIZE, class_driver->name, intf->minor - minor_base);
- device_destroy(usb_class->class, MKDEV(USB_MAJOR, intf->minor));
+ device_destroy(&usb_class, MKDEV(USB_MAJOR, intf->minor));
intf->usb_dev = NULL;
intf->minor = -1;
- destroy_usb_class();
}
EXPORT_SYMBOL(usb_deregister_dev);
--- a/drivers/usb/core/usb.c
+++ b/drivers/usb/core/usb.c
@@ -875,6 +875,9 @@ static int __init usb_init(void)
retval = bus_register(&usb_bus_type);
if (retval)
goto bus_register_failed;
+ retval = class_register(&usb_class);
+ if (retval)
+ goto class_register_failed;
retval = usb_host_init();
if (retval)
goto host_init_failed;
@@ -909,6 +912,8 @@ driver_register_failed:
major_init_failed:
usb_host_cleanup();
host_init_failed:
+ class_unregister(&usb_class);
+class_register_failed:
bus_unregister(&usb_bus_type);
bus_register_failed:
ksuspend_usb_cleanup();
@@ -932,6 +937,7 @@ static void __exit usb_exit(void)
usb_devio_cleanup();
usb_hub_cleanup();
usb_host_cleanup();
+ class_unregister(&usb_class);
bus_unregister(&usb_bus_type);
ksuspend_usb_cleanup();
}
--- a/drivers/usb/core/usb.h
+++ b/drivers/usb/core/usb.h
@@ -81,6 +81,7 @@ extern struct bus_type usb_bus_type;
extern struct device_type usb_device_type;
extern struct device_type usb_if_device_type;
extern struct usb_device_driver usb_generic_driver;
+extern struct class usb_class;
static inline int is_usb_device(const struct device *dev)
{
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -179,12 +179,18 @@ struct class {
struct module * owner;
struct subsystem subsys;
+ struct kset devices_dir;
struct list_head children;
struct list_head devices;
struct list_head interfaces;
struct kset class_dirs;
struct semaphore sem; /* locks both the children and interfaces lists */
+ /* possibly share the subsystem with a bus with the same name */
+ struct bus_type *bus;
+#ifdef CONFIG_SYSFS_DEPRECATED_FUTURE
+ struct kobject class_dir_depr;
+#endif
struct class_attribute * class_attrs;
struct class_device_attribute * class_dev_attrs;
struct device_attribute * dev_attrs;
@@ -271,8 +277,6 @@ struct class_device {
struct attribute_group ** groups; /* optional groups */
void (*release)(struct class_device *dev);
- int (*uevent)(struct class_device *dev, char **envp,
- int num_envp, char *buffer, int buffer_size);
char class_id[BUS_ID_SIZE]; /* unique to this class */
};