Commit e105b8bfc769b0545b6f0f395179d1e43cbee822

Authored by Dan Williams
Committed by Greg Kroah-Hartman
1 parent 93ded9b8fd

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>

Showing 7 changed files with 124 additions and 2 deletions Side-by-side Diff

Documentation/ABI/testing/sysfs-dev
  1 +What: /sys/dev
  2 +Date: April 2008
  3 +KernelVersion: 2.6.26
  4 +Contact: Dan Williams <dan.j.williams@intel.com>
  5 +Description: 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 +
  20 +Users: mdadm <linux-raid@vger.kernel.org>
Documentation/filesystems/sysfs.txt
... ... @@ -248,6 +248,7 @@
248 248 block/
249 249 bus/
250 250 class/
  251 +dev/
251 252 devices/
252 253 firmware/
253 254 net/
... ... @@ -274,6 +275,11 @@
274 275 filesystem wanting to export attributes must create its own hierarchy
275 276 below fs/ (see ./fuse.txt for an example).
276 277  
  278 +dev/ contains two directories char/ and block/. Inside these two
  279 +directories there are symlinks named <major>:<minor>. These symlinks
  280 +point to the sysfs directory for the given device. /sys/dev provides a
  281 +quick way to lookup the sysfs interface for a device from the result of
  282 +a stat(2) operation.
277 283  
278 284 More information can driver-model specific features can be found in
279 285 Documentation/driver-model/.
... ... @@ -370,7 +370,10 @@
370 370  
371 371 static 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 377 if (unlikely(error))
375 378 return error;
376 379 bdev_map = kobj_map_init(base_probe, &block_class_lock);
drivers/base/class.c
... ... @@ -148,6 +148,10 @@
148 148 if (error)
149 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 155 #if defined(CONFIG_SYSFS_DEPRECATED) && defined(CONFIG_BLOCK)
152 156 /* let the block class directory show up in the root of sysfs */
153 157 if (cls != &block_class)
... ... @@ -27,6 +27,9 @@
27 27  
28 28 int (*platform_notify)(struct device *dev) = NULL;
29 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 34 #ifdef CONFIG_BLOCK
32 35 static inline int device_is_not_partition(struct device *dev)
... ... @@ -776,6 +779,54 @@
776 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 830 * device_add - add device to device hierarchy.
780 831 * @dev: device.
781 832 *
... ... @@ -829,6 +880,10 @@
829 880 error = device_create_file(dev, &devt_attr);
830 881 if (error)
831 882 goto ueventattrError;
  883 +
  884 + error = device_create_sys_dev_entry(dev);
  885 + if (error)
  886 + goto devtattrError;
832 887 }
833 888  
834 889 error = device_add_class_symlinks(dev);
... ... @@ -873,6 +928,9 @@
873 928 device_remove_class_symlinks(dev);
874 929 SymlinkError:
875 930 if (MAJOR(dev->devt))
  931 + device_remove_sys_dev_entry(dev);
  932 + devtattrError:
  933 + if (MAJOR(dev->devt))
876 934 device_remove_file(dev, &devt_attr);
877 935 ueventattrError:
878 936 device_remove_file(dev, &uevent_attr);
879 937  
... ... @@ -948,8 +1006,10 @@
948 1006 device_pm_remove(dev);
949 1007 if (parent)
950 1008 klist_del(&dev->knode_parent);
951   - if (MAJOR(dev->devt))
  1009 + if (MAJOR(dev->devt)) {
  1010 + device_remove_sys_dev_entry(dev);
952 1011 device_remove_file(dev, &devt_attr);
  1012 + }
953 1013 if (dev->class) {
954 1014 device_remove_class_symlinks(dev);
955 1015  
956 1016  
... ... @@ -1074,7 +1134,25 @@
1074 1134 devices_kset = kset_create_and_add("devices", &device_uevent_ops, NULL);
1075 1135 if (!devices_kset)
1076 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 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 1158 EXPORT_SYMBOL_GPL(device_for_each_child);
... ... @@ -1447,5 +1525,8 @@
1447 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 }
drivers/usb/core/devio.c
... ... @@ -1792,6 +1792,11 @@
1792 1792 usb_classdev_class = NULL;
1793 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 1801 usb_register_notify(&usbdev_nb);
1797 1802 #endif
include/linux/device.h
... ... @@ -193,6 +193,7 @@
193 193 struct semaphore sem; /* locks children, devices, interfaces */
194 194 struct class_attribute *class_attrs;
195 195 struct device_attribute *dev_attrs;
  196 + struct kobject *dev_kobj;
196 197  
197 198 int (*dev_uevent)(struct device *dev, struct kobj_uevent_env *env);
198 199  
... ... @@ -205,6 +206,8 @@
205 206 struct pm_ops *pm;
206 207 };
207 208  
  209 +extern struct kobject *sysfs_dev_block_kobj;
  210 +extern struct kobject *sysfs_dev_char_kobj;
208 211 extern int __must_check class_register(struct class *class);
209 212 extern void class_unregister(struct class *class);
210 213 extern int class_for_each_device(struct class *class, void *data,