diff options
author | Dan Williams <dan.j.williams@intel.com> | 2008-04-21 13:51:07 -0400 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-07-22 00:54:40 -0400 |
commit | e105b8bfc769b0545b6f0f395179d1e43cbee822 (patch) | |
tree | 23a1f0839547829c4ca6e89cd40adf285eb9feb6 /drivers | |
parent | 93ded9b8fd42abe2c3607097963d8de6ad9117eb (diff) |
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>
Diffstat (limited to 'drivers')
-rw-r--r-- | drivers/base/class.c | 4 | ||||
-rw-r--r-- | drivers/base/core.c | 83 | ||||
-rw-r--r-- | drivers/usb/core/devio.c | 5 |
3 files changed, 91 insertions, 1 deletions
diff --git a/drivers/base/class.c b/drivers/base/class.c index e085af0ff94f..71ce3ff6bdf5 100644 --- a/drivers/base/class.c +++ b/drivers/base/class.c | |||
@@ -148,6 +148,10 @@ int class_register(struct class *cls) | |||
148 | if (error) | 148 | if (error) |
149 | return error; | 149 | return error; |
150 | 150 | ||
151 | /* set the default /sys/dev directory for devices of this class */ | ||
152 | if (!cls->dev_kobj) | ||
153 | cls->dev_kobj = sysfs_dev_char_kobj; | ||
154 | |||
151 | #if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK) | 155 | #if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK) |
152 | /* let the block class directory show up in the root of sysfs */ | 156 | /* let the block class directory show up in the root of sysfs */ |
153 | if (cls != &block_class) | 157 | if (cls != &block_class) |
diff --git a/drivers/base/core.c b/drivers/base/core.c index ee0a51a3a41d..be9aba4dc2fb 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c | |||
@@ -27,6 +27,9 @@ | |||
27 | 27 | ||
28 | int (*platform_notify)(struct device *dev) = NULL; | 28 | int (*platform_notify)(struct device *dev) = NULL; |
29 | int (*platform_notify_remove)(struct device *dev) = NULL; | 29 | int (*platform_notify_remove)(struct device *dev) = NULL; |
30 | static struct kobject *dev_kobj; | ||
31 | struct kobject *sysfs_dev_char_kobj; | ||
32 | struct kobject *sysfs_dev_block_kobj; | ||
30 | 33 | ||
31 | #ifdef CONFIG_BLOCK | 34 | #ifdef CONFIG_BLOCK |
32 | static inline int device_is_not_partition(struct device *dev) | 35 | static inline int device_is_not_partition(struct device *dev) |
@@ -776,6 +779,54 @@ int dev_set_name(struct device *dev, const char *fmt, ...) | |||
776 | EXPORT_SYMBOL_GPL(dev_set_name); | 779 | EXPORT_SYMBOL_GPL(dev_set_name); |
777 | 780 | ||
778 | /** | 781 | /** |
782 | * device_to_dev_kobj - select a /sys/dev/ directory for the device | ||
783 | * @dev: device | ||
784 | * | ||
785 | * By default we select char/ for new entries. Setting class->dev_obj | ||
786 | * to NULL prevents an entry from being created. class->dev_kobj must | ||
787 | * be set (or cleared) before any devices are registered to the class | ||
788 | * otherwise device_create_sys_dev_entry() and | ||
789 | * device_remove_sys_dev_entry() will disagree about the the presence | ||
790 | * of the link. | ||
791 | */ | ||
792 | static struct kobject *device_to_dev_kobj(struct device *dev) | ||
793 | { | ||
794 | struct kobject *kobj; | ||
795 | |||
796 | if (dev->class) | ||
797 | kobj = dev->class->dev_kobj; | ||
798 | else | ||
799 | kobj = sysfs_dev_char_kobj; | ||
800 | |||
801 | return kobj; | ||
802 | } | ||
803 | |||
804 | static int device_create_sys_dev_entry(struct device *dev) | ||
805 | { | ||
806 | struct kobject *kobj = device_to_dev_kobj(dev); | ||
807 | int error = 0; | ||
808 | char devt_str[15]; | ||
809 | |||
810 | if (kobj) { | ||
811 | format_dev_t(devt_str, dev->devt); | ||
812 | error = sysfs_create_link(kobj, &dev->kobj, devt_str); | ||
813 | } | ||
814 | |||
815 | return error; | ||
816 | } | ||
817 | |||
818 | static void device_remove_sys_dev_entry(struct device *dev) | ||
819 | { | ||
820 | struct kobject *kobj = device_to_dev_kobj(dev); | ||
821 | char devt_str[15]; | ||
822 | |||
823 | if (kobj) { | ||
824 | format_dev_t(devt_str, dev->devt); | ||
825 | sysfs_remove_link(kobj, devt_str); | ||
826 | } | ||
827 | } | ||
828 | |||
829 | /** | ||
779 | * device_add - add device to device hierarchy. | 830 | * device_add - add device to device hierarchy. |
780 | * @dev: device. | 831 | * @dev: device. |
781 | * | 832 | * |
@@ -829,6 +880,10 @@ int device_add(struct device *dev) | |||
829 | error = device_create_file(dev, &devt_attr); | 880 | error = device_create_file(dev, &devt_attr); |
830 | if (error) | 881 | if (error) |
831 | goto ueventattrError; | 882 | goto ueventattrError; |
883 | |||
884 | error = device_create_sys_dev_entry(dev); | ||
885 | if (error) | ||
886 | goto devtattrError; | ||
832 | } | 887 | } |
833 | 888 | ||
834 | error = device_add_class_symlinks(dev); | 889 | error = device_add_class_symlinks(dev); |
@@ -873,6 +928,9 @@ int device_add(struct device *dev) | |||
873 | device_remove_class_symlinks(dev); | 928 | device_remove_class_symlinks(dev); |
874 | SymlinkError: | 929 | SymlinkError: |
875 | if (MAJOR(dev->devt)) | 930 | if (MAJOR(dev->devt)) |
931 | device_remove_sys_dev_entry(dev); | ||
932 | devtattrError: | ||
933 | if (MAJOR(dev->devt)) | ||
876 | device_remove_file(dev, &devt_attr); | 934 | device_remove_file(dev, &devt_attr); |
877 | ueventattrError: | 935 | ueventattrError: |
878 | device_remove_file(dev, &uevent_attr); | 936 | device_remove_file(dev, &uevent_attr); |
@@ -948,8 +1006,10 @@ void device_del(struct device *dev) | |||
948 | device_pm_remove(dev); | 1006 | device_pm_remove(dev); |
949 | if (parent) | 1007 | if (parent) |
950 | klist_del(&dev->knode_parent); | 1008 | klist_del(&dev->knode_parent); |
951 | if (MAJOR(dev->devt)) | 1009 | if (MAJOR(dev->devt)) { |
1010 | device_remove_sys_dev_entry(dev); | ||
952 | device_remove_file(dev, &devt_attr); | 1011 | device_remove_file(dev, &devt_attr); |
1012 | } | ||
953 | if (dev->class) { | 1013 | if (dev->class) { |
954 | device_remove_class_symlinks(dev); | 1014 | device_remove_class_symlinks(dev); |
955 | 1015 | ||
@@ -1074,7 +1134,25 @@ int __init devices_init(void) | |||
1074 | devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL); | 1134 | devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL); |
1075 | if (!devices_kset) | 1135 | if (!devices_kset) |
1076 | return -ENOMEM; | 1136 | return -ENOMEM; |
1137 | dev_kobj = kobject_create_and_add("dev", NULL); | ||
1138 | if (!dev_kobj) | ||
1139 | goto dev_kobj_err; | ||
1140 | sysfs_dev_block_kobj = kobject_create_and_add("block", dev_kobj); | ||
1141 | if (!sysfs_dev_block_kobj) | ||
1142 | goto block_kobj_err; | ||
1143 | sysfs_dev_char_kobj = kobject_create_and_add("char", dev_kobj); | ||
1144 | if (!sysfs_dev_char_kobj) | ||
1145 | goto char_kobj_err; | ||
1146 | |||
1077 | return 0; | 1147 | return 0; |
1148 | |||
1149 | char_kobj_err: | ||
1150 | kobject_put(sysfs_dev_block_kobj); | ||
1151 | block_kobj_err: | ||
1152 | kobject_put(dev_kobj); | ||
1153 | dev_kobj_err: | ||
1154 | kset_unregister(devices_kset); | ||
1155 | return -ENOMEM; | ||
1078 | } | 1156 | } |
1079 | 1157 | ||
1080 | EXPORT_SYMBOL_GPL(device_for_each_child); | 1158 | EXPORT_SYMBOL_GPL(device_for_each_child); |
@@ -1447,4 +1525,7 @@ void device_shutdown(void) | |||
1447 | dev->driver->shutdown(dev); | 1525 | dev->driver->shutdown(dev); |
1448 | } | 1526 | } |
1449 | } | 1527 | } |
1528 | kobject_put(sysfs_dev_char_kobj); | ||
1529 | kobject_put(sysfs_dev_block_kobj); | ||
1530 | kobject_put(dev_kobj); | ||
1450 | } | 1531 | } |
diff --git a/drivers/usb/core/devio.c b/drivers/usb/core/devio.c index 54a350ccd033..6fbc8f5ab80c 100644 --- a/drivers/usb/core/devio.c +++ b/drivers/usb/core/devio.c | |||
@@ -1792,6 +1792,11 @@ int __init usb_devio_init(void) | |||
1792 | usb_classdev_class = NULL; | 1792 | usb_classdev_class = NULL; |
1793 | goto out; | 1793 | goto out; |
1794 | } | 1794 | } |
1795 | /* devices of this class shadow the major:minor of their parent | ||
1796 | * device, so clear ->dev_kobj to prevent adding duplicate entries | ||
1797 | * to /sys/dev | ||
1798 | */ | ||
1799 | usb_classdev_class->dev_kobj = NULL; | ||
1795 | 1800 | ||
1796 | usb_register_notify(&usbdev_nb); | 1801 | usb_register_notify(&usbdev_nb); |
1797 | #endif | 1802 | #endif |