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: Kay Sievers <kay.sievers@vrfy.org> Reviewed-by: SL Baur <steve@xemacs.org> Acked-by: Kay Sievers <kay.sievers@vrfy.org> Acked-by: Mark Lord <lkml@rtr.ca> Acked-by: H. Peter Anvin <hpa@zytor.com> Signed-off-by: Dan Williams <dan.j.williams@intel.com> Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
This commit is contained in:
parent
93ded9b8fd
commit
e105b8bfc7
20
Documentation/ABI/testing/sysfs-dev
Normal file
20
Documentation/ABI/testing/sysfs-dev
Normal file
@ -0,0 +1,20 @@
|
||||
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>
|
@ -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/.
|
||||
|
@ -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);
|
||||
|
@ -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)
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -1792,6 +1792,11 @@ int __init usb_devio_init(void)
|
||||
usb_classdev_class = NULL;
|
||||
goto out;
|
||||
}
|
||||
/* devices of this class shadow the major:minor of their parent
|
||||
* device, so clear ->dev_kobj to prevent adding duplicate entries
|
||||
* to /sys/dev
|
||||
*/
|
||||
usb_classdev_class->dev_kobj = NULL;
|
||||
|
||||
usb_register_notify(&usbdev_nb);
|
||||
#endif
|
||||
|
@ -193,6 +193,7 @@ struct class {
|
||||
struct semaphore sem; /* locks children, devices, interfaces */
|
||||
struct class_attribute *class_attrs;
|
||||
struct device_attribute *dev_attrs;
|
||||
struct kobject *dev_kobj;
|
||||
|
||||
int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
|
||||
|
||||
@ -205,6 +206,8 @@ struct class {
|
||||
struct pm_ops *pm;
|
||||
};
|
||||
|
||||
extern struct kobject *sysfs_dev_block_kobj;
|
||||
extern struct kobject *sysfs_dev_char_kobj;
|
||||
extern int __must_check class_register(struct class *class);
|
||||
extern void class_unregister(struct class *class);
|
||||
extern int class_for_each_device(struct class *class, void *data,
|
||||
|
Loading…
Reference in New Issue
Block a user