diff options
Diffstat (limited to 'drivers/base/bus.c')
| -rw-r--r-- | drivers/base/bus.c | 484 | 
1 files changed, 359 insertions, 125 deletions
diff --git a/drivers/base/bus.c b/drivers/base/bus.c index 33c270a64db..83e910a5756 100644 --- a/drivers/base/bus.c +++ b/drivers/base/bus.c @@ -16,11 +16,15 @@  #include <linux/slab.h>  #include <linux/init.h>  #include <linux/string.h> +#include <linux/mutex.h> +#include <linux/sysfs.h>  #include "base.h"  #include "power/power.h" +/* /sys/devices/system */ +static struct kset *system_kset; +  #define to_bus_attr(_attr) container_of(_attr, struct bus_attribute, attr) -#define to_bus(obj) container_of(obj, struct bus_type_private, subsys.kobj)  /*   * sysfs bindings for drivers @@ -96,11 +100,11 @@ static ssize_t bus_attr_show(struct kobject *kobj, struct attribute *attr,  			     char *buf)  {  	struct bus_attribute *bus_attr = to_bus_attr(attr); -	struct bus_type_private *bus_priv = to_bus(kobj); +	struct subsys_private *subsys_priv = to_subsys_private(kobj);  	ssize_t ret = 0;  	if (bus_attr->show) -		ret = bus_attr->show(bus_priv->bus, buf); +		ret = bus_attr->show(subsys_priv->bus, buf);  	return ret;  } @@ -108,11 +112,11 @@ static ssize_t bus_attr_store(struct kobject *kobj, struct attribute *attr,  			      const char *buf, size_t count)  {  	struct bus_attribute *bus_attr = to_bus_attr(attr); -	struct bus_type_private *bus_priv = to_bus(kobj); +	struct subsys_private *subsys_priv = to_subsys_private(kobj);  	ssize_t ret = 0;  	if (bus_attr->store) -		ret = bus_attr->store(bus_priv->bus, buf, count); +		ret = bus_attr->store(subsys_priv->bus, buf, count);  	return ret;  } @@ -142,8 +146,19 @@ void bus_remove_file(struct bus_type *bus, struct bus_attribute *attr)  }  EXPORT_SYMBOL_GPL(bus_remove_file); +static void bus_release(struct kobject *kobj) +{ +	struct subsys_private *priv = +		container_of(kobj, typeof(*priv), subsys.kobj); +	struct bus_type *bus = priv->bus; + +	kfree(priv); +	bus->p = NULL; +} +  static struct kobj_type bus_ktype = {  	.sysfs_ops	= &bus_sysfs_ops, +	.release	= bus_release,  };  static int bus_uevent_filter(struct kset *kset, struct kobject *kobj) @@ -161,11 +176,9 @@ static const struct kset_uevent_ops bus_uevent_ops = {  static struct kset *bus_kset; - -#ifdef CONFIG_HOTPLUG  /* Manually detach a device from its associated driver. */ -static ssize_t driver_unbind(struct device_driver *drv, -			     const char *buf, size_t count) +static ssize_t unbind_store(struct device_driver *drv, const char *buf, +			    size_t count)  {  	struct bus_type *bus = bus_get(drv->bus);  	struct device *dev; @@ -184,15 +197,15 @@ static ssize_t driver_unbind(struct device_driver *drv,  	bus_put(bus);  	return err;  } -static DRIVER_ATTR(unbind, S_IWUSR, NULL, driver_unbind); +static DRIVER_ATTR_WO(unbind);  /*   * Manually attach a device to a driver.   * Note: the driver must want to bind to the device,   * it is not possible to override the driver's id table.   */ -static ssize_t driver_bind(struct device_driver *drv, -			   const char *buf, size_t count) +static ssize_t bind_store(struct device_driver *drv, const char *buf, +			  size_t count)  {  	struct bus_type *bus = bus_get(drv->bus);  	struct device *dev; @@ -220,7 +233,7 @@ static ssize_t driver_bind(struct device_driver *drv,  	bus_put(bus);  	return err;  } -static DRIVER_ATTR(bind, S_IWUSR, NULL, driver_bind); +static DRIVER_ATTR_WO(bind);  static ssize_t show_drivers_autoprobe(struct bus_type *bus, char *buf)  { @@ -249,7 +262,6 @@ static ssize_t store_drivers_probe(struct bus_type *bus,  		return -EINVAL;  	return count;  } -#endif  static struct device *next_device(struct klist_iter *i)  { @@ -290,7 +302,7 @@ int bus_for_each_dev(struct bus_type *bus, struct device *start,  	struct device *dev;  	int error = 0; -	if (!bus) +	if (!bus || !bus->p)  		return -EINVAL;  	klist_iter_init_node(&bus->p->klist_devices, &i, @@ -324,7 +336,7 @@ struct device *bus_find_device(struct bus_type *bus,  	struct klist_iter i;  	struct device *dev; -	if (!bus) +	if (!bus || !bus->p)  		return NULL;  	klist_iter_init_node(&bus->p->klist_devices, &i, @@ -361,6 +373,47 @@ struct device *bus_find_device_by_name(struct bus_type *bus,  }  EXPORT_SYMBOL_GPL(bus_find_device_by_name); +/** + * subsys_find_device_by_id - find a device with a specific enumeration number + * @subsys: subsystem + * @id: index 'id' in struct device + * @hint: device to check first + * + * Check the hint's next object and if it is a match return it directly, + * otherwise, fall back to a full list search. Either way a reference for + * the returned object is taken. + */ +struct device *subsys_find_device_by_id(struct bus_type *subsys, unsigned int id, +					struct device *hint) +{ +	struct klist_iter i; +	struct device *dev; + +	if (!subsys) +		return NULL; + +	if (hint) { +		klist_iter_init_node(&subsys->p->klist_devices, &i, &hint->p->knode_bus); +		dev = next_device(&i); +		if (dev && dev->id == id && get_device(dev)) { +			klist_iter_exit(&i); +			return dev; +		} +		klist_iter_exit(&i); +	} + +	klist_iter_init_node(&subsys->p->klist_devices, &i, NULL); +	while ((dev = next_device(&i))) { +		if (dev->id == id && get_device(dev)) { +			klist_iter_exit(&i); +			return dev; +		} +	} +	klist_iter_exit(&i); +	return NULL; +} +EXPORT_SYMBOL_GPL(subsys_find_device_by_id); +  static struct device_driver *next_driver(struct klist_iter *i)  {  	struct klist_node *n = klist_next(i); @@ -419,7 +472,7 @@ static int device_add_attrs(struct bus_type *bus, struct device *dev)  	if (!bus->dev_attrs)  		return 0; -	for (i = 0; attr_name(bus->dev_attrs[i]); i++) { +	for (i = 0; bus->dev_attrs[i].attr.name; i++) {  		error = device_create_file(dev, &bus->dev_attrs[i]);  		if (error) {  			while (--i >= 0) @@ -435,7 +488,7 @@ static void device_remove_attrs(struct bus_type *bus, struct device *dev)  	int i;  	if (bus->dev_attrs) { -		for (i = 0; attr_name(bus->dev_attrs[i]); i++) +		for (i = 0; bus->dev_attrs[i].attr.name; i++)  			device_remove_file(dev, &bus->dev_attrs[i]);  	}  } @@ -458,6 +511,9 @@ int bus_add_device(struct device *dev)  		error = device_add_attrs(bus, dev);  		if (error)  			goto out_put; +		error = device_add_groups(dev, bus->dev_groups); +		if (error) +			goto out_groups;  		error = sysfs_create_link(&bus->p->devices_kset->kobj,  						&dev->kobj, dev_name(dev));  		if (error) @@ -472,6 +528,8 @@ int bus_add_device(struct device *dev)  out_subsys:  	sysfs_remove_link(&bus->p->devices_kset->kobj, dev_name(dev)); +out_groups: +	device_remove_groups(dev, bus->dev_groups);  out_id:  	device_remove_attrs(bus, dev);  out_put: @@ -488,76 +546,62 @@ out_put:  void bus_probe_device(struct device *dev)  {  	struct bus_type *bus = dev->bus; +	struct subsys_interface *sif;  	int ret; -	if (bus && bus->p->drivers_autoprobe) { +	if (!bus) +		return; + +	if (bus->p->drivers_autoprobe) {  		ret = device_attach(dev);  		WARN_ON(ret < 0);  	} + +	mutex_lock(&bus->p->mutex); +	list_for_each_entry(sif, &bus->p->interfaces, node) +		if (sif->add_dev) +			sif->add_dev(dev, sif); +	mutex_unlock(&bus->p->mutex);  }  /**   * bus_remove_device - remove device from bus   * @dev: device to be removed   * - * - Remove symlink from bus's directory. + * - Remove device from all interfaces. + * - Remove symlink from bus' directory.   * - Delete device from bus's list.   * - Detach from its driver.   * - Drop reference taken in bus_add_device().   */  void bus_remove_device(struct device *dev)  { -	if (dev->bus) { -		sysfs_remove_link(&dev->kobj, "subsystem"); -		sysfs_remove_link(&dev->bus->p->devices_kset->kobj, -				  dev_name(dev)); -		device_remove_attrs(dev->bus, dev); -		if (klist_node_attached(&dev->p->knode_bus)) -			klist_del(&dev->p->knode_bus); - -		pr_debug("bus: '%s': remove device %s\n", -			 dev->bus->name, dev_name(dev)); -		device_release_driver(dev); -		bus_put(dev->bus); -	} -} - -static int driver_add_attrs(struct bus_type *bus, struct device_driver *drv) -{ -	int error = 0; -	int i; - -	if (bus->drv_attrs) { -		for (i = 0; attr_name(bus->drv_attrs[i]); i++) { -			error = driver_create_file(drv, &bus->drv_attrs[i]); -			if (error) -				goto err; -		} -	} -done: -	return error; -err: -	while (--i >= 0) -		driver_remove_file(drv, &bus->drv_attrs[i]); -	goto done; -} +	struct bus_type *bus = dev->bus; +	struct subsys_interface *sif; -static void driver_remove_attrs(struct bus_type *bus, -				struct device_driver *drv) -{ -	int i; +	if (!bus) +		return; -	if (bus->drv_attrs) { -		for (i = 0; attr_name(bus->drv_attrs[i]); i++) -			driver_remove_file(drv, &bus->drv_attrs[i]); -	} +	mutex_lock(&bus->p->mutex); +	list_for_each_entry(sif, &bus->p->interfaces, node) +		if (sif->remove_dev) +			sif->remove_dev(dev, sif); +	mutex_unlock(&bus->p->mutex); + +	sysfs_remove_link(&dev->kobj, "subsystem"); +	sysfs_remove_link(&dev->bus->p->devices_kset->kobj, +			  dev_name(dev)); +	device_remove_attrs(dev->bus, dev); +	device_remove_groups(dev, dev->bus->dev_groups); +	if (klist_node_attached(&dev->p->knode_bus)) +		klist_del(&dev->p->knode_bus); + +	pr_debug("bus: '%s': remove device %s\n", +		 dev->bus->name, dev_name(dev)); +	device_release_driver(dev); +	bus_put(dev->bus);  } -#ifdef CONFIG_HOTPLUG -/* - * Thanks to drivers making their tables __devinit, we can't allow manual - * bind and unbind from userspace unless CONFIG_HOTPLUG is enabled. - */  static int __must_check add_bind_files(struct device_driver *drv)  {  	int ret; @@ -601,15 +645,9 @@ static void remove_probe_files(struct bus_type *bus)  	bus_remove_file(bus, &bus_attr_drivers_autoprobe);  	bus_remove_file(bus, &bus_attr_drivers_probe);  } -#else -static inline int add_bind_files(struct device_driver *drv) { return 0; } -static inline void remove_bind_files(struct device_driver *drv) {} -static inline int add_probe_files(struct bus_type *bus) { return 0; } -static inline void remove_probe_files(struct bus_type *bus) {} -#endif - -static ssize_t driver_uevent_store(struct device_driver *drv, -				   const char *buf, size_t count) + +static ssize_t uevent_store(struct device_driver *drv, const char *buf, +			    size_t count)  {  	enum kobject_action action; @@ -617,7 +655,7 @@ static ssize_t driver_uevent_store(struct device_driver *drv,  		kobject_uevent(&drv->p->kobj, action);  	return count;  } -static DRIVER_ATTR(uevent, S_IWUSR, NULL, driver_uevent_store); +static DRIVER_ATTR_WO(uevent);  /**   * bus_add_driver - Add a driver to the bus. @@ -649,12 +687,12 @@ int bus_add_driver(struct device_driver *drv)  	if (error)  		goto out_unregister; +	klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);  	if (drv->bus->p->drivers_autoprobe) {  		error = driver_attach(drv);  		if (error)  			goto out_unregister;  	} -	klist_add_tail(&priv->knode_bus, &bus->p->klist_drivers);  	module_add_driver(drv->owner, drv);  	error = driver_create_file(drv, &driver_attr_uevent); @@ -662,10 +700,10 @@ int bus_add_driver(struct device_driver *drv)  		printk(KERN_ERR "%s: uevent attr (%s) failed\n",  			__func__, drv->name);  	} -	error = driver_add_attrs(bus, drv); +	error = driver_add_groups(drv, bus->drv_groups);  	if (error) {  		/* How the hell do we get out of this pickle? Give up */ -		printk(KERN_ERR "%s: driver_add_attrs(%s) failed\n", +		printk(KERN_ERR "%s: driver_create_groups(%s) failed\n",  			__func__, drv->name);  	} @@ -678,7 +716,6 @@ int bus_add_driver(struct device_driver *drv)  		}  	} -	kobject_uevent(&priv->kobj, KOBJ_ADD);  	return 0;  out_unregister: @@ -705,7 +742,7 @@ void bus_remove_driver(struct device_driver *drv)  	if (!drv->suppress_bind_attrs)  		remove_bind_files(drv); -	driver_remove_attrs(drv->bus, drv); +	driver_remove_groups(drv, drv->bus->drv_groups);  	driver_remove_file(drv, &driver_attr_uevent);  	klist_remove(&drv->p->knode_bus);  	pr_debug("bus: '%s': remove driver %s\n", drv->bus->name, drv->name); @@ -784,40 +821,16 @@ struct bus_type *find_bus(char *name)  }  #endif  /*  0  */ - -/** - * bus_add_attrs - Add default attributes for this bus. - * @bus: Bus that has just been registered. - */ - -static int bus_add_attrs(struct bus_type *bus) +static int bus_add_groups(struct bus_type *bus, +			  const struct attribute_group **groups)  { -	int error = 0; -	int i; - -	if (bus->bus_attrs) { -		for (i = 0; attr_name(bus->bus_attrs[i]); i++) { -			error = bus_create_file(bus, &bus->bus_attrs[i]); -			if (error) -				goto err; -		} -	} -done: -	return error; -err: -	while (--i >= 0) -		bus_remove_file(bus, &bus->bus_attrs[i]); -	goto done; +	return sysfs_create_groups(&bus->p->subsys.kobj, groups);  } -static void bus_remove_attrs(struct bus_type *bus) +static void bus_remove_groups(struct bus_type *bus, +			      const struct attribute_group **groups)  { -	int i; - -	if (bus->bus_attrs) { -		for (i = 0; attr_name(bus->bus_attrs[i]); i++) -			bus_remove_file(bus, &bus->bus_attrs[i]); -	} +	sysfs_remove_groups(&bus->p->subsys.kobj, groups);  }  static void klist_devices_get(struct klist_node *n) @@ -848,19 +861,20 @@ static ssize_t bus_uevent_store(struct bus_type *bus,  static BUS_ATTR(uevent, S_IWUSR, NULL, bus_uevent_store);  /** - * bus_register - register a bus with the system. - * @bus: bus. + * bus_register - register a driver-core subsystem + * @bus: bus to register   * - * Once we have that, we registered the bus with the kobject + * Once we have that, we register the bus with the kobject   * infrastructure, then register the children subsystems it has: - * the devices and drivers that belong to the bus. + * the devices and drivers that belong to the subsystem.   */  int bus_register(struct bus_type *bus)  {  	int retval; -	struct bus_type_private *priv; +	struct subsys_private *priv; +	struct lock_class_key *key = &bus->lock_key; -	priv = kzalloc(sizeof(struct bus_type_private), GFP_KERNEL); +	priv = kzalloc(sizeof(struct subsys_private), GFP_KERNEL);  	if (!priv)  		return -ENOMEM; @@ -899,6 +913,8 @@ int bus_register(struct bus_type *bus)  		goto bus_drivers_fail;  	} +	INIT_LIST_HEAD(&priv->interfaces); +	__mutex_init(&priv->mutex, "subsys mutex", key);  	klist_init(&priv->klist_devices, klist_devices_get, klist_devices_put);  	klist_init(&priv->klist_drivers, NULL, NULL); @@ -906,14 +922,14 @@ int bus_register(struct bus_type *bus)  	if (retval)  		goto bus_probe_files_fail; -	retval = bus_add_attrs(bus); +	retval = bus_add_groups(bus, bus->bus_groups);  	if (retval) -		goto bus_attrs_fail; +		goto bus_groups_fail;  	pr_debug("bus: '%s': registered\n", bus->name);  	return 0; -bus_attrs_fail: +bus_groups_fail:  	remove_probe_files(bus);  bus_probe_files_fail:  	kset_unregister(bus->p->drivers_kset); @@ -940,14 +956,14 @@ EXPORT_SYMBOL_GPL(bus_register);  void bus_unregister(struct bus_type *bus)  {  	pr_debug("bus: '%s': unregistering\n", bus->name); -	bus_remove_attrs(bus); +	if (bus->dev_root) +		device_unregister(bus->dev_root); +	bus_remove_groups(bus, bus->bus_groups);  	remove_probe_files(bus);  	kset_unregister(bus->p->drivers_kset);  	kset_unregister(bus->p->devices_kset);  	bus_remove_file(bus, &bus_attr_uevent);  	kset_unregister(&bus->p->subsys); -	kfree(bus->p); -	bus->p = NULL;  }  EXPORT_SYMBOL_GPL(bus_unregister); @@ -976,7 +992,7 @@ struct klist *bus_get_device_klist(struct bus_type *bus)  EXPORT_SYMBOL_GPL(bus_get_device_klist);  /* - * Yes, this forcably breaks the klist abstraction temporarily.  It + * Yes, this forcibly breaks the klist abstraction temporarily.  It   * just wants to sort the klist, not change reference counts and   * take/drop locks rapidly in the process.  It does all this while   * holding the lock for the list, so objects can't otherwise be @@ -1029,10 +1045,228 @@ void bus_sort_breadthfirst(struct bus_type *bus,  }  EXPORT_SYMBOL_GPL(bus_sort_breadthfirst); +/** + * subsys_dev_iter_init - initialize subsys device iterator + * @iter: subsys iterator to initialize + * @subsys: the subsys we wanna iterate over + * @start: the device to start iterating from, if any + * @type: device_type of the devices to iterate over, NULL for all + * + * Initialize subsys iterator @iter such that it iterates over devices + * of @subsys.  If @start is set, the list iteration will start there, + * otherwise if it is NULL, the iteration starts at the beginning of + * the list. + */ +void subsys_dev_iter_init(struct subsys_dev_iter *iter, struct bus_type *subsys, +			  struct device *start, const struct device_type *type) +{ +	struct klist_node *start_knode = NULL; + +	if (start) +		start_knode = &start->p->knode_bus; +	klist_iter_init_node(&subsys->p->klist_devices, &iter->ki, start_knode); +	iter->type = type; +} +EXPORT_SYMBOL_GPL(subsys_dev_iter_init); + +/** + * subsys_dev_iter_next - iterate to the next device + * @iter: subsys iterator to proceed + * + * Proceed @iter to the next device and return it.  Returns NULL if + * iteration is complete. + * + * The returned device is referenced and won't be released till + * iterator is proceed to the next device or exited.  The caller is + * free to do whatever it wants to do with the device including + * calling back into subsys code. + */ +struct device *subsys_dev_iter_next(struct subsys_dev_iter *iter) +{ +	struct klist_node *knode; +	struct device *dev; + +	for (;;) { +		knode = klist_next(&iter->ki); +		if (!knode) +			return NULL; +		dev = container_of(knode, struct device_private, knode_bus)->device; +		if (!iter->type || iter->type == dev->type) +			return dev; +	} +} +EXPORT_SYMBOL_GPL(subsys_dev_iter_next); + +/** + * subsys_dev_iter_exit - finish iteration + * @iter: subsys iterator to finish + * + * Finish an iteration.  Always call this function after iteration is + * complete whether the iteration ran till the end or not. + */ +void subsys_dev_iter_exit(struct subsys_dev_iter *iter) +{ +	klist_iter_exit(&iter->ki); +} +EXPORT_SYMBOL_GPL(subsys_dev_iter_exit); + +int subsys_interface_register(struct subsys_interface *sif) +{ +	struct bus_type *subsys; +	struct subsys_dev_iter iter; +	struct device *dev; + +	if (!sif || !sif->subsys) +		return -ENODEV; + +	subsys = bus_get(sif->subsys); +	if (!subsys) +		return -EINVAL; + +	mutex_lock(&subsys->p->mutex); +	list_add_tail(&sif->node, &subsys->p->interfaces); +	if (sif->add_dev) { +		subsys_dev_iter_init(&iter, subsys, NULL, NULL); +		while ((dev = subsys_dev_iter_next(&iter))) +			sif->add_dev(dev, sif); +		subsys_dev_iter_exit(&iter); +	} +	mutex_unlock(&subsys->p->mutex); + +	return 0; +} +EXPORT_SYMBOL_GPL(subsys_interface_register); + +void subsys_interface_unregister(struct subsys_interface *sif) +{ +	struct bus_type *subsys; +	struct subsys_dev_iter iter; +	struct device *dev; + +	if (!sif || !sif->subsys) +		return; + +	subsys = sif->subsys; + +	mutex_lock(&subsys->p->mutex); +	list_del_init(&sif->node); +	if (sif->remove_dev) { +		subsys_dev_iter_init(&iter, subsys, NULL, NULL); +		while ((dev = subsys_dev_iter_next(&iter))) +			sif->remove_dev(dev, sif); +		subsys_dev_iter_exit(&iter); +	} +	mutex_unlock(&subsys->p->mutex); + +	bus_put(subsys); +} +EXPORT_SYMBOL_GPL(subsys_interface_unregister); + +static void system_root_device_release(struct device *dev) +{ +	kfree(dev); +} + +static int subsys_register(struct bus_type *subsys, +			   const struct attribute_group **groups, +			   struct kobject *parent_of_root) +{ +	struct device *dev; +	int err; + +	err = bus_register(subsys); +	if (err < 0) +		return err; + +	dev = kzalloc(sizeof(struct device), GFP_KERNEL); +	if (!dev) { +		err = -ENOMEM; +		goto err_dev; +	} + +	err = dev_set_name(dev, "%s", subsys->name); +	if (err < 0) +		goto err_name; + +	dev->kobj.parent = parent_of_root; +	dev->groups = groups; +	dev->release = system_root_device_release; + +	err = device_register(dev); +	if (err < 0) +		goto err_dev_reg; + +	subsys->dev_root = dev; +	return 0; + +err_dev_reg: +	put_device(dev); +	dev = NULL; +err_name: +	kfree(dev); +err_dev: +	bus_unregister(subsys); +	return err; +} + +/** + * subsys_system_register - register a subsystem at /sys/devices/system/ + * @subsys: system subsystem + * @groups: default attributes for the root device + * + * All 'system' subsystems have a /sys/devices/system/<name> root device + * with the name of the subsystem. The root device can carry subsystem- + * wide attributes. All registered devices are below this single root + * device and are named after the subsystem with a simple enumeration + * number appended. The registered devices are not explicitly named; + * only 'id' in the device needs to be set. + * + * Do not use this interface for anything new, it exists for compatibility + * with bad ideas only. New subsystems should use plain subsystems; and + * add the subsystem-wide attributes should be added to the subsystem + * directory itself and not some create fake root-device placed in + * /sys/devices/system/<name>. + */ +int subsys_system_register(struct bus_type *subsys, +			   const struct attribute_group **groups) +{ +	return subsys_register(subsys, groups, &system_kset->kobj); +} +EXPORT_SYMBOL_GPL(subsys_system_register); + +/** + * subsys_virtual_register - register a subsystem at /sys/devices/virtual/ + * @subsys: virtual subsystem + * @groups: default attributes for the root device + * + * All 'virtual' subsystems have a /sys/devices/system/<name> root device + * with the name of the subystem.  The root device can carry subsystem-wide + * attributes.  All registered devices are below this single root device. + * There's no restriction on device naming.  This is for kernel software + * constructs which need sysfs interface. + */ +int subsys_virtual_register(struct bus_type *subsys, +			    const struct attribute_group **groups) +{ +	struct kobject *virtual_dir; + +	virtual_dir = virtual_device_parent(NULL); +	if (!virtual_dir) +		return -ENOMEM; + +	return subsys_register(subsys, groups, virtual_dir); +} +EXPORT_SYMBOL_GPL(subsys_virtual_register); +  int __init buses_init(void)  {  	bus_kset = kset_create_and_add("bus", &bus_uevent_ops, NULL);  	if (!bus_kset)  		return -ENOMEM; + +	system_kset = kset_create_and_add("system", NULL, &devices_kset->kobj); +	if (!system_kset) +		return -ENOMEM; +  	return 0;  }  | 
