Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e105b8bf authored by Dan Williams's avatar Dan Williams Committed by Greg Kroah-Hartman
Browse files

sysfs: add /sys/dev/{char,block} to lookup sysfs path by major:minor



Why?:
There are occasions where userspace would like to access sysfs
attributes for a device but it may not know how sysfs has named the
device or the path.  For example what is the sysfs path for
/dev/disk/by-id/ata-ST3160827AS_5MT004CK?  With this change a call to
stat(2) returns the major:minor then userspace can see that
/sys/dev/block/8:32 links to /sys/block/sdc.

What are the alternatives?:
1/ Add an ioctl to return the path: Doable, but sysfs is meant to reduce
   the need to proliferate ioctl interfaces into the kernel, so this
   seems counter productive.

2/ Use udev to create these symlinks: Also doable, but it adds a
   udev dependency to utilities that might be running in a limited
   environment like an initramfs.

3/ Do a full-tree search of sysfs.

[kay.sievers@vrfy.org: fix duplicate registrations]
[kay.sievers@vrfy.org: cleanup suggestions]

Cc: Neil Brown <neilb@suse.de>
Cc: Tejun Heo <htejun@gmail.com>
Acked-by: default avatarKay Sievers <kay.sievers@vrfy.org>
Reviewed-by: default avatarSL Baur <steve@xemacs.org>
Acked-by: default avatarKay Sievers <kay.sievers@vrfy.org>
Acked-by: default avatarMark Lord <lkml@rtr.ca>
Acked-by: default avatarH. Peter Anvin <hpa@zytor.com>
Signed-off-by: default avatarDan Williams <dan.j.williams@intel.com>
Signed-off-by: default avatarGreg Kroah-Hartman <gregkh@suse.de>
parent 93ded9b8
Loading
Loading
Loading
Loading
+20 −0
Original line number Diff line number Diff line
What:		/sys/dev
Date:		April 2008
KernelVersion:	2.6.26
Contact:	Dan Williams <dan.j.williams@intel.com>
Description:	The /sys/dev tree provides a method to look up the sysfs
		path for a device using the information returned from
		stat(2).  There are two directories, 'block' and 'char',
		beneath /sys/dev containing symbolic links with names of
		the form "<major>:<minor>".  These links point to the
		corresponding sysfs path for the given device.

		Example:
		$ readlink /sys/dev/block/8:32
		../../block/sdc

		Entries in /sys/dev/char and /sys/dev/block will be
		dynamically created and destroyed as devices enter and
		leave the system.

Users:		mdadm <linux-raid@vger.kernel.org>
+6 −0
Original line number Diff line number Diff line
@@ -248,6 +248,7 @@ The top level sysfs directory looks like:
block/
bus/
class/
dev/
devices/
firmware/
net/
@@ -274,6 +275,11 @@ fs/ contains a directory for some filesystems. Currently each
filesystem wanting to export attributes must create its own hierarchy
below fs/ (see ./fuse.txt for an example).

dev/ contains two directories char/ and block/. Inside these two
directories there are symlinks named <major>:<minor>.  These symlinks
point to the sysfs directory for the given device.  /sys/dev provides a
quick way to lookup the sysfs interface for a device from the result of
a stat(2) operation.

More information can driver-model specific features can be found in
Documentation/driver-model/. 
+4 −1
Original line number Diff line number Diff line
@@ -370,7 +370,10 @@ static struct kobject *base_probe(dev_t devt, int *part, void *data)

static int __init genhd_device_init(void)
{
	int error = class_register(&block_class);
	int error;

	block_class.dev_kobj = sysfs_dev_block_kobj;
	error = class_register(&block_class);
	if (unlikely(error))
		return error;
	bdev_map = kobj_map_init(base_probe, &block_class_lock);
+4 −0
Original line number Diff line number Diff line
@@ -148,6 +148,10 @@ int class_register(struct class *cls)
	if (error)
		return error;

	/* set the default /sys/dev directory for devices of this class */
	if (!cls->dev_kobj)
		cls->dev_kobj = sysfs_dev_char_kobj;

#if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK)
	/* let the block class directory show up in the root of sysfs */
	if (cls != &block_class)
+82 −1
Original line number Diff line number Diff line
@@ -27,6 +27,9 @@

int (*platform_notify)(struct device *dev) = NULL;
int (*platform_notify_remove)(struct device *dev) = NULL;
static struct kobject *dev_kobj;
struct kobject *sysfs_dev_char_kobj;
struct kobject *sysfs_dev_block_kobj;

#ifdef CONFIG_BLOCK
static inline int device_is_not_partition(struct device *dev)
@@ -775,6 +778,54 @@ int dev_set_name(struct device *dev, const char *fmt, ...)
}
EXPORT_SYMBOL_GPL(dev_set_name);

/**
 * device_to_dev_kobj - select a /sys/dev/ directory for the device
 * @dev: device
 *
 * By default we select char/ for new entries.  Setting class->dev_obj
 * to NULL prevents an entry from being created.  class->dev_kobj must
 * be set (or cleared) before any devices are registered to the class
 * otherwise device_create_sys_dev_entry() and
 * device_remove_sys_dev_entry() will disagree about the the presence
 * of the link.
 */
static struct kobject *device_to_dev_kobj(struct device *dev)
{
	struct kobject *kobj;

	if (dev->class)
		kobj = dev->class->dev_kobj;
	else
		kobj = sysfs_dev_char_kobj;

	return kobj;
}

static int device_create_sys_dev_entry(struct device *dev)
{
	struct kobject *kobj = device_to_dev_kobj(dev);
	int error = 0;
	char devt_str[15];

	if (kobj) {
		format_dev_t(devt_str, dev->devt);
		error = sysfs_create_link(kobj, &dev->kobj, devt_str);
	}

	return error;
}

static void device_remove_sys_dev_entry(struct device *dev)
{
	struct kobject *kobj = device_to_dev_kobj(dev);
	char devt_str[15];

	if (kobj) {
		format_dev_t(devt_str, dev->devt);
		sysfs_remove_link(kobj, devt_str);
	}
}

/**
 * device_add - add device to device hierarchy.
 * @dev: device.
@@ -829,6 +880,10 @@ int device_add(struct device *dev)
		error = device_create_file(dev, &devt_attr);
		if (error)
			goto ueventattrError;

		error = device_create_sys_dev_entry(dev);
		if (error)
			goto devtattrError;
	}

	error = device_add_class_symlinks(dev);
@@ -872,6 +927,9 @@ int device_add(struct device *dev)
 AttrsError:
	device_remove_class_symlinks(dev);
 SymlinkError:
	if (MAJOR(dev->devt))
		device_remove_sys_dev_entry(dev);
 devtattrError:
	if (MAJOR(dev->devt))
		device_remove_file(dev, &devt_attr);
 ueventattrError:
@@ -948,8 +1006,10 @@ void device_del(struct device *dev)
	device_pm_remove(dev);
	if (parent)
		klist_del(&dev->knode_parent);
	if (MAJOR(dev->devt))
	if (MAJOR(dev->devt)) {
		device_remove_sys_dev_entry(dev);
		device_remove_file(dev, &devt_attr);
	}
	if (dev->class) {
		device_remove_class_symlinks(dev);

@@ -1074,7 +1134,25 @@ int __init devices_init(void)
	devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
	if (!devices_kset)
		return -ENOMEM;
	dev_kobj = kobject_create_and_add("dev", NULL);
	if (!dev_kobj)
		goto dev_kobj_err;
	sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj);
	if (!sysfs_dev_block_kobj)
		goto block_kobj_err;
	sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj);
	if (!sysfs_dev_char_kobj)
		goto char_kobj_err;

	return 0;

 char_kobj_err:
	kobject_put(sysfs_dev_block_kobj);
 block_kobj_err:
	kobject_put(dev_kobj);
 dev_kobj_err:
	kset_unregister(devices_kset);
	return -ENOMEM;
}

EXPORT_SYMBOL_GPL(device_for_each_child);
@@ -1447,4 +1525,7 @@ void device_shutdown(void)
			dev->driver->shutdown(dev);
		}
	}
	kobject_put(sysfs_dev_char_kobj);
	kobject_put(sysfs_dev_block_kobj);
	kobject_put(dev_kobj);
}
Loading