aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDan Williams <dan.j.williams@intel.com>2008-04-21 13:51:07 -0400
committerGreg Kroah-Hartman <gregkh@suse.de>2008-07-22 00:54:40 -0400
commite105b8bfc769b0545b6f0f395179d1e43cbee822 (patch)
tree23a1f0839547829c4ca6e89cd40adf285eb9feb6
parent93ded9b8fd42abe2c3607097963d8de6ad9117eb (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>
-rw-r--r--Documentation/ABI/testing/sysfs-dev20
-rw-r--r--Documentation/filesystems/sysfs.txt6
-rw-r--r--block/genhd.c5
-rw-r--r--drivers/base/class.c4
-rw-r--r--drivers/base/core.c83
-rw-r--r--drivers/usb/core/devio.c5
-rw-r--r--include/linux/device.h3
7 files changed, 124 insertions, 2 deletions
diff --git a/Documentation/ABI/testing/sysfs-dev b/Documentation/ABI/testing/sysfs-dev
new file mode 100644
index 000000000000..a9f2b8b0530f
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-dev
@@ -0,0 +1,20 @@
1What: /sys/dev
2Date: April 2008
3KernelVersion: 2.6.26
4Contact: Dan Williams <dan.j.williams@intel.com>
5Description: The /sys/dev tree provides a method to look up the sysfs
6 path for a device using the information returned from
7 stat(2). There are two directories, 'block' and 'char',
8 beneath /sys/dev containing symbolic links with names of
9 the form "<major>:<minor>". These links point to the
10 corresponding sysfs path for the given device.
11
12 Example:
13 $ readlink /sys/dev/block/8:32
14 ../../block/sdc
15
16 Entries in /sys/dev/char and /sys/dev/block will be
17 dynamically created and destroyed as devices enter and
18 leave the system.
19
20Users: mdadm <linux-raid@vger.kernel.org>
diff --git a/Documentation/filesystems/sysfs.txt b/Documentation/filesystems/sysfs.txt
index 7f27b8f840d0..9e9c348275a9 100644
--- a/Documentation/filesystems/sysfs.txt
+++ b/Documentation/filesystems/sysfs.txt
@@ -248,6 +248,7 @@ The top level sysfs directory looks like:
248block/ 248block/
249bus/ 249bus/
250class/ 250class/
251dev/
251devices/ 252devices/
252firmware/ 253firmware/
253net/ 254net/
@@ -274,6 +275,11 @@ fs/ contains a directory for some filesystems. Currently each
274filesystem wanting to export attributes must create its own hierarchy 275filesystem wanting to export attributes must create its own hierarchy
275below fs/ (see ./fuse.txt for an example). 276below fs/ (see ./fuse.txt for an example).
276 277
278dev/ contains two directories char/ and block/. Inside these two
279directories there are symlinks named <major>:<minor>. These symlinks
280point to the sysfs directory for the given device. /sys/dev provides a
281quick way to lookup the sysfs interface for a device from the result of
282a stat(2) operation.
277 283
278More information can driver-model specific features can be found in 284More information can driver-model specific features can be found in
279Documentation/driver-model/. 285Documentation/driver-model/.
diff --git a/block/genhd.c b/block/genhd.c
index 9074f384b097..24e3fc9095fe 100644
--- a/block/genhd.c
+++ b/block/genhd.c
@@ -370,7 +370,10 @@ static struct kobject *base_probe(dev_t devt, int *part, void *data)
370 370
371static int __init genhd_device_init(void) 371static int __init genhd_device_init(void)
372{ 372{
373 int error = class_register(&block_class); 373 int error;
374
375 block_class.dev_kobj = sysfs_dev_block_kobj;
376 error = class_register(&block_class);
374 if (unlikely(error)) 377 if (unlikely(error))
375 return error; 378 return error;
376 bdev_map = kobj_map_init(base_probe, &block_class_lock); 379 bdev_map = kobj_map_init(base_probe, &block_class_lock);
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
28int (*platform_notify)(struct device *dev) = NULL; 28int (*platform_notify)(struct device *dev) = NULL;
29int (*platform_notify_remove)(struct device *dev) = NULL; 29int (*platform_notify_remove)(struct device *dev) = NULL;
30static struct kobject *dev_kobj;
31struct kobject *sysfs_dev_char_kobj;
32struct kobject *sysfs_dev_block_kobj;
30 33
31#ifdef CONFIG_BLOCK 34#ifdef CONFIG_BLOCK
32static inline int device_is_not_partition(struct device *dev) 35static inline int device_is_not_partition(struct device *dev)
@@ -776,6 +779,54 @@ int dev_set_name(struct device *dev, const char *fmt, ...)
776EXPORT_SYMBOL_GPL(dev_set_name); 779EXPORT_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 */
792static 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
804static 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
818static 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
1080EXPORT_SYMBOL_GPL(device_for_each_child); 1158EXPORT_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
diff --git a/include/linux/device.h b/include/linux/device.h
index f71a78d123ae..e49aa74f248c 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -193,6 +193,7 @@ struct class {
193 struct semaphore sem; /* locks children, devices, interfaces */ 193 struct semaphore sem; /* locks children, devices, interfaces */
194 struct class_attribute *class_attrs; 194 struct class_attribute *class_attrs;
195 struct device_attribute *dev_attrs; 195 struct device_attribute *dev_attrs;
196 struct kobject *dev_kobj;
196 197
197 int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env); 198 int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
198 199
@@ -205,6 +206,8 @@ struct class {
205 struct pm_ops *pm; 206 struct pm_ops *pm;
206}; 207};
207 208
209extern struct kobject *sysfs_dev_block_kobj;
210extern struct kobject *sysfs_dev_char_kobj;
208extern int __must_check class_register(struct class *class); 211extern int __must_check class_register(struct class *class);
209extern void class_unregister(struct class *class); 212extern void class_unregister(struct class *class);
210extern int class_for_each_device(struct class *class, void *data, 213extern int class_for_each_device(struct class *class, void *data,