diff options
Diffstat (limited to 'drivers/edac/edac_mc_sysfs.c')
-rw-r--r-- | drivers/edac/edac_mc_sysfs.c | 175 |
1 files changed, 149 insertions, 26 deletions
diff --git a/drivers/edac/edac_mc_sysfs.c b/drivers/edac/edac_mc_sysfs.c index 418b65f1a1da..c200c2fd43ea 100644 --- a/drivers/edac/edac_mc_sysfs.c +++ b/drivers/edac/edac_mc_sysfs.c | |||
@@ -557,6 +557,8 @@ static ssize_t mcidev_show(struct kobject *kobj, struct attribute *attr, | |||
557 | struct mem_ctl_info *mem_ctl_info = to_mci(kobj); | 557 | struct mem_ctl_info *mem_ctl_info = to_mci(kobj); |
558 | struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); | 558 | struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); |
559 | 559 | ||
560 | debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); | ||
561 | |||
560 | if (mcidev_attr->show) | 562 | if (mcidev_attr->show) |
561 | return mcidev_attr->show(mem_ctl_info, buffer); | 563 | return mcidev_attr->show(mem_ctl_info, buffer); |
562 | 564 | ||
@@ -569,6 +571,8 @@ static ssize_t mcidev_store(struct kobject *kobj, struct attribute *attr, | |||
569 | struct mem_ctl_info *mem_ctl_info = to_mci(kobj); | 571 | struct mem_ctl_info *mem_ctl_info = to_mci(kobj); |
570 | struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); | 572 | struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); |
571 | 573 | ||
574 | debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); | ||
575 | |||
572 | if (mcidev_attr->store) | 576 | if (mcidev_attr->store) |
573 | return mcidev_attr->store(mem_ctl_info, buffer, count); | 577 | return mcidev_attr->store(mem_ctl_info, buffer, count); |
574 | 578 | ||
@@ -726,28 +730,118 @@ void edac_mc_unregister_sysfs_main_kobj(struct mem_ctl_info *mci) | |||
726 | 730 | ||
727 | #define EDAC_DEVICE_SYMLINK "device" | 731 | #define EDAC_DEVICE_SYMLINK "device" |
728 | 732 | ||
733 | #define grp_to_mci(k) (container_of(k, struct mcidev_sysfs_group_kobj, kobj)->mci) | ||
734 | |||
735 | /* MCI show/store functions for top most object */ | ||
736 | static ssize_t inst_grp_show(struct kobject *kobj, struct attribute *attr, | ||
737 | char *buffer) | ||
738 | { | ||
739 | struct mem_ctl_info *mem_ctl_info = grp_to_mci(kobj); | ||
740 | struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); | ||
741 | |||
742 | debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); | ||
743 | |||
744 | if (mcidev_attr->show) | ||
745 | return mcidev_attr->show(mem_ctl_info, buffer); | ||
746 | |||
747 | return -EIO; | ||
748 | } | ||
749 | |||
750 | static ssize_t inst_grp_store(struct kobject *kobj, struct attribute *attr, | ||
751 | const char *buffer, size_t count) | ||
752 | { | ||
753 | struct mem_ctl_info *mem_ctl_info = grp_to_mci(kobj); | ||
754 | struct mcidev_sysfs_attribute *mcidev_attr = to_mcidev_attr(attr); | ||
755 | |||
756 | debugf1("%s() mem_ctl_info %p\n", __func__, mem_ctl_info); | ||
757 | |||
758 | if (mcidev_attr->store) | ||
759 | return mcidev_attr->store(mem_ctl_info, buffer, count); | ||
760 | |||
761 | return -EIO; | ||
762 | } | ||
763 | |||
764 | /* No memory to release for this kobj */ | ||
765 | static void edac_inst_grp_release(struct kobject *kobj) | ||
766 | { | ||
767 | struct mcidev_sysfs_group_kobj *grp; | ||
768 | struct mem_ctl_info *mci; | ||
769 | |||
770 | debugf1("%s()\n", __func__); | ||
771 | |||
772 | grp = container_of(kobj, struct mcidev_sysfs_group_kobj, kobj); | ||
773 | mci = grp->mci; | ||
774 | |||
775 | kobject_put(&mci->edac_mci_kobj); | ||
776 | } | ||
777 | |||
778 | /* Intermediate show/store table */ | ||
779 | static struct sysfs_ops inst_grp_ops = { | ||
780 | .show = inst_grp_show, | ||
781 | .store = inst_grp_store | ||
782 | }; | ||
783 | |||
784 | /* the kobj_type instance for a instance group */ | ||
785 | static struct kobj_type ktype_inst_grp = { | ||
786 | .release = edac_inst_grp_release, | ||
787 | .sysfs_ops = &inst_grp_ops, | ||
788 | }; | ||
789 | |||
790 | |||
729 | /* | 791 | /* |
730 | * edac_create_mci_instance_attributes | 792 | * edac_create_mci_instance_attributes |
731 | * create MC driver specific attributes at the topmost level | 793 | * create MC driver specific attributes bellow an specified kobj |
732 | * directory of this mci instance. | 794 | * This routine calls itself recursively, in order to create an entire |
795 | * object tree. | ||
733 | */ | 796 | */ |
734 | static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci) | 797 | static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci, |
798 | struct mcidev_sysfs_attribute *sysfs_attrib, | ||
799 | struct kobject *kobj) | ||
735 | { | 800 | { |
736 | int err; | 801 | int err; |
737 | struct mcidev_sysfs_attribute *sysfs_attrib; | ||
738 | 802 | ||
739 | /* point to the start of the array and iterate over it | 803 | debugf1("%s()\n", __func__); |
740 | * adding each attribute listed to this mci instance's kobject | 804 | |
741 | */ | 805 | while (sysfs_attrib) { |
742 | sysfs_attrib = mci->mc_driver_sysfs_attributes; | 806 | if (sysfs_attrib->grp) { |
807 | struct mcidev_sysfs_group_kobj *grp_kobj; | ||
808 | |||
809 | grp_kobj = kzalloc(sizeof(*grp_kobj), GFP_KERNEL); | ||
810 | if (!grp_kobj) | ||
811 | return -ENOMEM; | ||
812 | |||
813 | list_add_tail(&grp_kobj->list, &mci->grp_kobj_list); | ||
814 | |||
815 | grp_kobj->grp = sysfs_attrib->grp; | ||
816 | grp_kobj->mci = mci; | ||
817 | |||
818 | debugf0("%s() grp %s, mci %p\n", __func__, | ||
819 | sysfs_attrib->grp->name, mci); | ||
820 | |||
821 | err = kobject_init_and_add(&grp_kobj->kobj, | ||
822 | &ktype_inst_grp, | ||
823 | &mci->edac_mci_kobj, | ||
824 | sysfs_attrib->grp->name); | ||
825 | if (err) | ||
826 | return err; | ||
827 | |||
828 | err = edac_create_mci_instance_attributes(mci, | ||
829 | grp_kobj->grp->mcidev_attr, | ||
830 | &grp_kobj->kobj); | ||
831 | |||
832 | if (err) | ||
833 | return err; | ||
834 | } else if (sysfs_attrib->attr.name) { | ||
835 | debugf0("%s() file %s\n", __func__, | ||
836 | sysfs_attrib->attr.name); | ||
837 | |||
838 | err = sysfs_create_file(kobj, &sysfs_attrib->attr); | ||
839 | } else | ||
840 | break; | ||
743 | 841 | ||
744 | while (sysfs_attrib && sysfs_attrib->attr.name) { | ||
745 | err = sysfs_create_file(&mci->edac_mci_kobj, | ||
746 | (struct attribute*) sysfs_attrib); | ||
747 | if (err) { | 842 | if (err) { |
748 | return err; | 843 | return err; |
749 | } | 844 | } |
750 | |||
751 | sysfs_attrib++; | 845 | sysfs_attrib++; |
752 | } | 846 | } |
753 | 847 | ||
@@ -759,21 +853,44 @@ static int edac_create_mci_instance_attributes(struct mem_ctl_info *mci) | |||
759 | * remove MC driver specific attributes at the topmost level | 853 | * remove MC driver specific attributes at the topmost level |
760 | * directory of this mci instance. | 854 | * directory of this mci instance. |
761 | */ | 855 | */ |
762 | static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci) | 856 | static void edac_remove_mci_instance_attributes(struct mem_ctl_info *mci, |
857 | struct mcidev_sysfs_attribute *sysfs_attrib, | ||
858 | struct kobject *kobj, int count) | ||
763 | { | 859 | { |
764 | struct mcidev_sysfs_attribute *sysfs_attrib; | 860 | struct mcidev_sysfs_group_kobj *grp_kobj, *tmp; |
765 | 861 | ||
766 | /* point to the start of the array and iterate over it | 862 | debugf1("%s()\n", __func__); |
767 | * adding each attribute listed to this mci instance's kobject | ||
768 | */ | ||
769 | sysfs_attrib = mci->mc_driver_sysfs_attributes; | ||
770 | 863 | ||
771 | /* loop if there are attributes and until we hit a NULL entry */ | 864 | /* |
772 | while (sysfs_attrib && sysfs_attrib->attr.name) { | 865 | * loop if there are attributes and until we hit a NULL entry |
773 | sysfs_remove_file(&mci->edac_mci_kobj, | 866 | * Remove first all the atributes |
774 | (struct attribute *) sysfs_attrib); | 867 | */ |
868 | while (sysfs_attrib) { | ||
869 | if (sysfs_attrib->grp) { | ||
870 | list_for_each_entry(grp_kobj, &mci->grp_kobj_list, | ||
871 | list) | ||
872 | if (grp_kobj->grp == sysfs_attrib->grp) | ||
873 | edac_remove_mci_instance_attributes(mci, | ||
874 | grp_kobj->grp->mcidev_attr, | ||
875 | &grp_kobj->kobj, count + 1); | ||
876 | } else if (sysfs_attrib->attr.name) { | ||
877 | debugf0("%s() file %s\n", __func__, | ||
878 | sysfs_attrib->attr.name); | ||
879 | sysfs_remove_file(kobj, &sysfs_attrib->attr); | ||
880 | } else | ||
881 | break; | ||
775 | sysfs_attrib++; | 882 | sysfs_attrib++; |
776 | } | 883 | } |
884 | |||
885 | /* | ||
886 | * Now that all attributes got removed, it is save to remove all groups | ||
887 | */ | ||
888 | if (!count) | ||
889 | list_for_each_entry_safe(grp_kobj, tmp, &mci->grp_kobj_list, | ||
890 | list) { | ||
891 | debugf0("%s() grp %s\n", __func__, grp_kobj->grp->name); | ||
892 | kobject_put(&grp_kobj->kobj); | ||
893 | } | ||
777 | } | 894 | } |
778 | 895 | ||
779 | 896 | ||
@@ -794,6 +911,8 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) | |||
794 | 911 | ||
795 | debugf0("%s() idx=%d\n", __func__, mci->mc_idx); | 912 | debugf0("%s() idx=%d\n", __func__, mci->mc_idx); |
796 | 913 | ||
914 | INIT_LIST_HEAD(&mci->grp_kobj_list); | ||
915 | |||
797 | /* create a symlink for the device */ | 916 | /* create a symlink for the device */ |
798 | err = sysfs_create_link(kobj_mci, &mci->dev->kobj, | 917 | err = sysfs_create_link(kobj_mci, &mci->dev->kobj, |
799 | EDAC_DEVICE_SYMLINK); | 918 | EDAC_DEVICE_SYMLINK); |
@@ -806,7 +925,9 @@ int edac_create_sysfs_mci_device(struct mem_ctl_info *mci) | |||
806 | * then create them now for the driver. | 925 | * then create them now for the driver. |
807 | */ | 926 | */ |
808 | if (mci->mc_driver_sysfs_attributes) { | 927 | if (mci->mc_driver_sysfs_attributes) { |
809 | err = edac_create_mci_instance_attributes(mci); | 928 | err = edac_create_mci_instance_attributes(mci, |
929 | mci->mc_driver_sysfs_attributes, | ||
930 | &mci->edac_mci_kobj); | ||
810 | if (err) { | 931 | if (err) { |
811 | debugf1("%s() failure to create mci attributes\n", | 932 | debugf1("%s() failure to create mci attributes\n", |
812 | __func__); | 933 | __func__); |
@@ -841,7 +962,8 @@ fail1: | |||
841 | } | 962 | } |
842 | 963 | ||
843 | /* remove the mci instance's attributes, if any */ | 964 | /* remove the mci instance's attributes, if any */ |
844 | edac_remove_mci_instance_attributes(mci); | 965 | edac_remove_mci_instance_attributes(mci, |
966 | mci->mc_driver_sysfs_attributes, &mci->edac_mci_kobj, 0); | ||
845 | 967 | ||
846 | /* remove the symlink */ | 968 | /* remove the symlink */ |
847 | sysfs_remove_link(kobj_mci, EDAC_DEVICE_SYMLINK); | 969 | sysfs_remove_link(kobj_mci, EDAC_DEVICE_SYMLINK); |
@@ -875,8 +997,9 @@ void edac_remove_sysfs_mci_device(struct mem_ctl_info *mci) | |||
875 | debugf0("%s() remove_mci_instance\n", __func__); | 997 | debugf0("%s() remove_mci_instance\n", __func__); |
876 | 998 | ||
877 | /* remove this mci instance's attribtes */ | 999 | /* remove this mci instance's attribtes */ |
878 | edac_remove_mci_instance_attributes(mci); | 1000 | edac_remove_mci_instance_attributes(mci, |
879 | 1001 | mci->mc_driver_sysfs_attributes, | |
1002 | &mci->edac_mci_kobj, 0); | ||
880 | debugf0("%s() unregister this mci kobj\n", __func__); | 1003 | debugf0("%s() unregister this mci kobj\n", __func__); |
881 | 1004 | ||
882 | /* unregister this instance's kobject */ | 1005 | /* unregister this instance's kobject */ |