diff options
author | Kay Sievers <kay.sievers@vrfy.org> | 2007-11-21 11:29:15 -0500 |
---|---|---|
committer | Greg Kroah-Hartman <gregkh@suse.de> | 2008-01-24 23:40:36 -0500 |
commit | da231fd5d113ab6da5dab7a2d2c38d0a540f939c (patch) | |
tree | fbaa6dfe56d544af0edc3e01633006515c2e582d /drivers/base | |
parent | ef2c51746dc89c2326ce522f8fb8a57695780e75 (diff) |
Driver core: fix class glue dir cleanup logic
We should remove the glue directory between the class and the bus
device _after_ we sent out the 'remove' event for the device, otherwise
the parent relationship is no longer valid, and composing the path
with deleted sysfs entries will not work.
Cc: Alan Stern <stern@rowland.harvard.edu>
Signed-off-by: Kay Sievers <kay.sievers@vrfy.org>
Signed-off-by: Greg Kroah-Hartman <gregkh@suse.de>
Diffstat (limited to 'drivers/base')
-rw-r--r-- | drivers/base/core.c | 206 |
1 files changed, 94 insertions, 112 deletions
diff --git a/drivers/base/core.c b/drivers/base/core.c index 22fdf320a2a6..13cae18936c5 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c | |||
@@ -18,7 +18,7 @@ | |||
18 | #include <linux/string.h> | 18 | #include <linux/string.h> |
19 | #include <linux/kdev_t.h> | 19 | #include <linux/kdev_t.h> |
20 | #include <linux/notifier.h> | 20 | #include <linux/notifier.h> |
21 | 21 | #include <linux/genhd.h> | |
22 | #include <asm/semaphore.h> | 22 | #include <asm/semaphore.h> |
23 | 23 | ||
24 | #include "base.h" | 24 | #include "base.h" |
@@ -538,22 +538,20 @@ void device_initialize(struct device *dev) | |||
538 | } | 538 | } |
539 | 539 | ||
540 | #ifdef CONFIG_SYSFS_DEPRECATED | 540 | #ifdef CONFIG_SYSFS_DEPRECATED |
541 | static struct kobject * get_device_parent(struct device *dev, | 541 | static struct kobject *get_device_parent(struct device *dev, |
542 | struct device *parent) | 542 | struct device *parent) |
543 | { | 543 | { |
544 | /* | 544 | /* class devices without a parent live in /sys/class/<classname>/ */ |
545 | * Set the parent to the class, not the parent device | ||
546 | * for topmost devices in class hierarchy. | ||
547 | * This keeps sysfs from having a symlink to make old | ||
548 | * udevs happy | ||
549 | */ | ||
550 | if (dev->class && (!parent || parent->class != dev->class)) | 545 | if (dev->class && (!parent || parent->class != dev->class)) |
551 | return &dev->class->subsys.kobj; | 546 | return &dev->class->subsys.kobj; |
547 | /* all other devices keep their parent */ | ||
552 | else if (parent) | 548 | else if (parent) |
553 | return &parent->kobj; | 549 | return &parent->kobj; |
554 | 550 | ||
555 | return NULL; | 551 | return NULL; |
556 | } | 552 | } |
553 | |||
554 | static inline void cleanup_device_parent(struct device *dev) {} | ||
557 | #else | 555 | #else |
558 | static struct kobject *virtual_device_parent(struct device *dev) | 556 | static struct kobject *virtual_device_parent(struct device *dev) |
559 | { | 557 | { |
@@ -566,8 +564,8 @@ static struct kobject *virtual_device_parent(struct device *dev) | |||
566 | return virtual_dir; | 564 | return virtual_dir; |
567 | } | 565 | } |
568 | 566 | ||
569 | static struct kobject * get_device_parent(struct device *dev, | 567 | static struct kobject *get_device_parent(struct device *dev, |
570 | struct device *parent) | 568 | struct device *parent) |
571 | { | 569 | { |
572 | int retval; | 570 | int retval; |
573 | 571 | ||
@@ -618,6 +616,34 @@ static struct kobject * get_device_parent(struct device *dev, | |||
618 | return &parent->kobj; | 616 | return &parent->kobj; |
619 | return NULL; | 617 | return NULL; |
620 | } | 618 | } |
619 | |||
620 | static void cleanup_device_parent(struct device *dev) | ||
621 | { | ||
622 | struct device *d; | ||
623 | int other = 0; | ||
624 | |||
625 | if (!dev->class) | ||
626 | return; | ||
627 | |||
628 | /* see if we live in a parent class directory */ | ||
629 | if (dev->kobj.parent->kset != &dev->class->class_dirs) | ||
630 | return; | ||
631 | |||
632 | /* if we are the last child of our class, delete the directory */ | ||
633 | down(&dev->class->sem); | ||
634 | list_for_each_entry(d, &dev->class->devices, node) { | ||
635 | if (d == dev) | ||
636 | continue; | ||
637 | if (d->kobj.parent == dev->kobj.parent) { | ||
638 | other = 1; | ||
639 | break; | ||
640 | } | ||
641 | } | ||
642 | if (!other) | ||
643 | kobject_del(dev->kobj.parent); | ||
644 | kobject_put(dev->kobj.parent); | ||
645 | up(&dev->class->sem); | ||
646 | } | ||
621 | #endif | 647 | #endif |
622 | 648 | ||
623 | static int setup_parent(struct device *dev, struct device *parent) | 649 | static int setup_parent(struct device *dev, struct device *parent) |
@@ -637,65 +663,74 @@ static int device_add_class_symlinks(struct device *dev) | |||
637 | 663 | ||
638 | if (!dev->class) | 664 | if (!dev->class) |
639 | return 0; | 665 | return 0; |
666 | |||
640 | error = sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj, | 667 | error = sysfs_create_link(&dev->kobj, &dev->class->subsys.kobj, |
641 | "subsystem"); | 668 | "subsystem"); |
642 | if (error) | 669 | if (error) |
643 | goto out; | 670 | goto out; |
644 | /* | 671 | |
645 | * If this is not a "fake" compatible device, then create the | 672 | #ifdef CONFIG_SYSFS_DEPRECATED |
646 | * symlink from the class to the device. | 673 | /* stacked class devices need a symlink in the class directory */ |
647 | */ | ||
648 | if (dev->kobj.parent != &dev->class->subsys.kobj) { | 674 | if (dev->kobj.parent != &dev->class->subsys.kobj) { |
649 | error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, | 675 | error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, |
650 | dev->bus_id); | 676 | dev->bus_id); |
651 | if (error) | 677 | if (error) |
652 | goto out_subsys; | 678 | goto out_subsys; |
653 | } | 679 | } |
680 | |||
654 | if (dev->parent) { | 681 | if (dev->parent) { |
655 | #ifdef CONFIG_SYSFS_DEPRECATED | 682 | struct device *parent = dev->parent; |
656 | { | 683 | char *class_name; |
657 | struct device *parent = dev->parent; | ||
658 | char *class_name; | ||
659 | |||
660 | /* | ||
661 | * In old sysfs stacked class devices had 'device' | ||
662 | * link pointing to real device instead of parent | ||
663 | */ | ||
664 | while (parent->class && !parent->bus && parent->parent) | ||
665 | parent = parent->parent; | ||
666 | |||
667 | error = sysfs_create_link(&dev->kobj, | ||
668 | &parent->kobj, | ||
669 | "device"); | ||
670 | if (error) | ||
671 | goto out_busid; | ||
672 | 684 | ||
673 | class_name = make_class_name(dev->class->name, | 685 | /* |
674 | &dev->kobj); | 686 | * stacked class devices have the 'device' link |
675 | if (class_name) | 687 | * pointing to the bus device instead of the parent |
676 | error = sysfs_create_link(&dev->parent->kobj, | 688 | */ |
677 | &dev->kobj, class_name); | 689 | while (parent->class && !parent->bus && parent->parent) |
678 | kfree(class_name); | 690 | parent = parent->parent; |
679 | if (error) | 691 | |
680 | goto out_device; | 692 | error = sysfs_create_link(&dev->kobj, |
681 | } | 693 | &parent->kobj, |
682 | #else | ||
683 | error = sysfs_create_link(&dev->kobj, &dev->parent->kobj, | ||
684 | "device"); | 694 | "device"); |
685 | if (error) | 695 | if (error) |
686 | goto out_busid; | 696 | goto out_busid; |
687 | #endif | 697 | |
698 | class_name = make_class_name(dev->class->name, | ||
699 | &dev->kobj); | ||
700 | if (class_name) | ||
701 | error = sysfs_create_link(&dev->parent->kobj, | ||
702 | &dev->kobj, class_name); | ||
703 | kfree(class_name); | ||
704 | if (error) | ||
705 | goto out_device; | ||
688 | } | 706 | } |
689 | return 0; | 707 | return 0; |
690 | 708 | ||
691 | #ifdef CONFIG_SYSFS_DEPRECATED | ||
692 | out_device: | 709 | out_device: |
693 | if (dev->parent) | 710 | if (dev->parent) |
694 | sysfs_remove_link(&dev->kobj, "device"); | 711 | sysfs_remove_link(&dev->kobj, "device"); |
695 | #endif | ||
696 | out_busid: | 712 | out_busid: |
697 | if (dev->kobj.parent != &dev->class->subsys.kobj) | 713 | if (dev->kobj.parent != &dev->class->subsys.kobj) |
698 | sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); | 714 | sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); |
715 | #else | ||
716 | /* link in the class directory pointing to the device */ | ||
717 | error = sysfs_create_link(&dev->class->subsys.kobj, &dev->kobj, | ||
718 | dev->bus_id); | ||
719 | if (error) | ||
720 | goto out_subsys; | ||
721 | |||
722 | if (dev->parent) { | ||
723 | error = sysfs_create_link(&dev->kobj, &dev->parent->kobj, | ||
724 | "device"); | ||
725 | if (error) | ||
726 | goto out_busid; | ||
727 | } | ||
728 | return 0; | ||
729 | |||
730 | out_busid: | ||
731 | sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); | ||
732 | #endif | ||
733 | |||
699 | out_subsys: | 734 | out_subsys: |
700 | sysfs_remove_link(&dev->kobj, "subsystem"); | 735 | sysfs_remove_link(&dev->kobj, "subsystem"); |
701 | out: | 736 | out: |
@@ -706,8 +741,9 @@ static void device_remove_class_symlinks(struct device *dev) | |||
706 | { | 741 | { |
707 | if (!dev->class) | 742 | if (!dev->class) |
708 | return; | 743 | return; |
709 | if (dev->parent) { | 744 | |
710 | #ifdef CONFIG_SYSFS_DEPRECATED | 745 | #ifdef CONFIG_SYSFS_DEPRECATED |
746 | if (dev->parent) { | ||
711 | char *class_name; | 747 | char *class_name; |
712 | 748 | ||
713 | class_name = make_class_name(dev->class->name, &dev->kobj); | 749 | class_name = make_class_name(dev->class->name, &dev->kobj); |
@@ -715,11 +751,18 @@ static void device_remove_class_symlinks(struct device *dev) | |||
715 | sysfs_remove_link(&dev->parent->kobj, class_name); | 751 | sysfs_remove_link(&dev->parent->kobj, class_name); |
716 | kfree(class_name); | 752 | kfree(class_name); |
717 | } | 753 | } |
718 | #endif | ||
719 | sysfs_remove_link(&dev->kobj, "device"); | 754 | sysfs_remove_link(&dev->kobj, "device"); |
720 | } | 755 | } |
756 | |||
721 | if (dev->kobj.parent != &dev->class->subsys.kobj) | 757 | if (dev->kobj.parent != &dev->class->subsys.kobj) |
722 | sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); | 758 | sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); |
759 | #else | ||
760 | if (dev->parent) | ||
761 | sysfs_remove_link(&dev->kobj, "device"); | ||
762 | |||
763 | sysfs_remove_link(&dev->class->subsys.kobj, dev->bus_id); | ||
764 | #endif | ||
765 | |||
723 | sysfs_remove_link(&dev->kobj, "subsystem"); | 766 | sysfs_remove_link(&dev->kobj, "subsystem"); |
724 | } | 767 | } |
725 | 768 | ||
@@ -830,26 +873,6 @@ int device_add(struct device *dev) | |||
830 | SymlinkError: | 873 | SymlinkError: |
831 | if (MAJOR(dev->devt)) | 874 | if (MAJOR(dev->devt)) |
832 | device_remove_file(dev, &devt_attr); | 875 | device_remove_file(dev, &devt_attr); |
833 | |||
834 | if (dev->class) { | ||
835 | sysfs_remove_link(&dev->kobj, "subsystem"); | ||
836 | /* If this is not a "fake" compatible device, remove the | ||
837 | * symlink from the class to the device. */ | ||
838 | if (dev->kobj.parent != &dev->class->subsys.kobj) | ||
839 | sysfs_remove_link(&dev->class->subsys.kobj, | ||
840 | dev->bus_id); | ||
841 | if (parent) { | ||
842 | #ifdef CONFIG_SYSFS_DEPRECATED | ||
843 | char *class_name = make_class_name(dev->class->name, | ||
844 | &dev->kobj); | ||
845 | if (class_name) | ||
846 | sysfs_remove_link(&dev->parent->kobj, | ||
847 | class_name); | ||
848 | kfree(class_name); | ||
849 | #endif | ||
850 | sysfs_remove_link(&dev->kobj, "device"); | ||
851 | } | ||
852 | } | ||
853 | ueventattrError: | 876 | ueventattrError: |
854 | device_remove_file(dev, &uevent_attr); | 877 | device_remove_file(dev, &uevent_attr); |
855 | attrError: | 878 | attrError: |
@@ -932,23 +955,7 @@ void device_del(struct device * dev) | |||
932 | if (MAJOR(dev->devt)) | 955 | if (MAJOR(dev->devt)) |
933 | device_remove_file(dev, &devt_attr); | 956 | device_remove_file(dev, &devt_attr); |
934 | if (dev->class) { | 957 | if (dev->class) { |
935 | sysfs_remove_link(&dev->kobj, "subsystem"); | 958 | device_remove_class_symlinks(dev); |
936 | /* If this is not a "fake" compatible device, remove the | ||
937 | * symlink from the class to the device. */ | ||
938 | if (dev->kobj.parent != &dev->class->subsys.kobj) | ||
939 | sysfs_remove_link(&dev->class->subsys.kobj, | ||
940 | dev->bus_id); | ||
941 | if (parent) { | ||
942 | #ifdef CONFIG_SYSFS_DEPRECATED | ||
943 | char *class_name = make_class_name(dev->class->name, | ||
944 | &dev->kobj); | ||
945 | if (class_name) | ||
946 | sysfs_remove_link(&dev->parent->kobj, | ||
947 | class_name); | ||
948 | kfree(class_name); | ||
949 | #endif | ||
950 | sysfs_remove_link(&dev->kobj, "device"); | ||
951 | } | ||
952 | 959 | ||
953 | down(&dev->class->sem); | 960 | down(&dev->class->sem); |
954 | /* notify any interfaces that the device is now gone */ | 961 | /* notify any interfaces that the device is now gone */ |
@@ -958,31 +965,6 @@ void device_del(struct device * dev) | |||
958 | /* remove the device from the class list */ | 965 | /* remove the device from the class list */ |
959 | list_del_init(&dev->node); | 966 | list_del_init(&dev->node); |
960 | up(&dev->class->sem); | 967 | up(&dev->class->sem); |
961 | |||
962 | /* If we live in a parent class-directory, unreference it */ | ||
963 | if (dev->kobj.parent->kset == &dev->class->class_dirs) { | ||
964 | struct device *d; | ||
965 | int other = 0; | ||
966 | |||
967 | /* | ||
968 | * if we are the last child of our class, delete | ||
969 | * our class-directory at this parent | ||
970 | */ | ||
971 | down(&dev->class->sem); | ||
972 | list_for_each_entry(d, &dev->class->devices, node) { | ||
973 | if (d == dev) | ||
974 | continue; | ||
975 | if (d->kobj.parent == dev->kobj.parent) { | ||
976 | other = 1; | ||
977 | break; | ||
978 | } | ||
979 | } | ||
980 | if (!other) | ||
981 | kobject_del(dev->kobj.parent); | ||
982 | |||
983 | kobject_put(dev->kobj.parent); | ||
984 | up(&dev->class->sem); | ||
985 | } | ||
986 | } | 968 | } |
987 | device_remove_file(dev, &uevent_attr); | 969 | device_remove_file(dev, &uevent_attr); |
988 | device_remove_attrs(dev); | 970 | device_remove_attrs(dev); |
@@ -1004,9 +986,9 @@ void device_del(struct device * dev) | |||
1004 | blocking_notifier_call_chain(&dev->bus->p->bus_notifier, | 986 | blocking_notifier_call_chain(&dev->bus->p->bus_notifier, |
1005 | BUS_NOTIFY_DEL_DEVICE, dev); | 987 | BUS_NOTIFY_DEL_DEVICE, dev); |
1006 | kobject_uevent(&dev->kobj, KOBJ_REMOVE); | 988 | kobject_uevent(&dev->kobj, KOBJ_REMOVE); |
989 | cleanup_device_parent(dev); | ||
1007 | kobject_del(&dev->kobj); | 990 | kobject_del(&dev->kobj); |
1008 | if (parent) | 991 | put_device(parent); |
1009 | put_device(parent); | ||
1010 | } | 992 | } |
1011 | 993 | ||
1012 | /** | 994 | /** |