diff options
-rw-r--r-- | drivers/edac/edac_mc.c | 25 |
1 files changed, 21 insertions, 4 deletions
diff --git a/drivers/edac/edac_mc.c b/drivers/edac/edac_mc.c index 78d8c7d6e76a..34eb9703ed33 100644 --- a/drivers/edac/edac_mc.c +++ b/drivers/edac/edac_mc.c | |||
@@ -42,6 +42,12 @@ | |||
42 | static DEFINE_MUTEX(mem_ctls_mutex); | 42 | static DEFINE_MUTEX(mem_ctls_mutex); |
43 | static LIST_HEAD(mc_devices); | 43 | static LIST_HEAD(mc_devices); |
44 | 44 | ||
45 | /* | ||
46 | * Used to lock EDAC MC to just one module, avoiding two drivers e. g. | ||
47 | * apei/ghes and i7core_edac to be used at the same time. | ||
48 | */ | ||
49 | static void const *edac_mc_owner; | ||
50 | |||
45 | unsigned edac_dimm_info_location(struct dimm_info *dimm, char *buf, | 51 | unsigned edac_dimm_info_location(struct dimm_info *dimm, char *buf, |
46 | unsigned len) | 52 | unsigned len) |
47 | { | 53 | { |
@@ -659,9 +665,9 @@ fail1: | |||
659 | return 1; | 665 | return 1; |
660 | } | 666 | } |
661 | 667 | ||
662 | static void del_mc_from_global_list(struct mem_ctl_info *mci) | 668 | static int del_mc_from_global_list(struct mem_ctl_info *mci) |
663 | { | 669 | { |
664 | atomic_dec(&edac_handlers); | 670 | int handlers = atomic_dec_return(&edac_handlers); |
665 | list_del_rcu(&mci->link); | 671 | list_del_rcu(&mci->link); |
666 | 672 | ||
667 | /* these are for safe removal of devices from global list while | 673 | /* these are for safe removal of devices from global list while |
@@ -669,6 +675,8 @@ static void del_mc_from_global_list(struct mem_ctl_info *mci) | |||
669 | */ | 675 | */ |
670 | synchronize_rcu(); | 676 | synchronize_rcu(); |
671 | INIT_LIST_HEAD(&mci->link); | 677 | INIT_LIST_HEAD(&mci->link); |
678 | |||
679 | return handlers; | ||
672 | } | 680 | } |
673 | 681 | ||
674 | /** | 682 | /** |
@@ -712,6 +720,7 @@ EXPORT_SYMBOL(edac_mc_find); | |||
712 | /* FIXME - should a warning be printed if no error detection? correction? */ | 720 | /* FIXME - should a warning be printed if no error detection? correction? */ |
713 | int edac_mc_add_mc(struct mem_ctl_info *mci) | 721 | int edac_mc_add_mc(struct mem_ctl_info *mci) |
714 | { | 722 | { |
723 | int ret = -EINVAL; | ||
715 | edac_dbg(0, "\n"); | 724 | edac_dbg(0, "\n"); |
716 | 725 | ||
717 | #ifdef CONFIG_EDAC_DEBUG | 726 | #ifdef CONFIG_EDAC_DEBUG |
@@ -742,6 +751,11 @@ int edac_mc_add_mc(struct mem_ctl_info *mci) | |||
742 | #endif | 751 | #endif |
743 | mutex_lock(&mem_ctls_mutex); | 752 | mutex_lock(&mem_ctls_mutex); |
744 | 753 | ||
754 | if (edac_mc_owner && edac_mc_owner != mci->mod_name) { | ||
755 | ret = -EPERM; | ||
756 | goto fail0; | ||
757 | } | ||
758 | |||
745 | if (add_mc_to_global_list(mci)) | 759 | if (add_mc_to_global_list(mci)) |
746 | goto fail0; | 760 | goto fail0; |
747 | 761 | ||
@@ -768,6 +782,8 @@ int edac_mc_add_mc(struct mem_ctl_info *mci) | |||
768 | edac_mc_printk(mci, KERN_INFO, "Giving out device to '%s' '%s':" | 782 | edac_mc_printk(mci, KERN_INFO, "Giving out device to '%s' '%s':" |
769 | " DEV %s\n", mci->mod_name, mci->ctl_name, edac_dev_name(mci)); | 783 | " DEV %s\n", mci->mod_name, mci->ctl_name, edac_dev_name(mci)); |
770 | 784 | ||
785 | edac_mc_owner = mci->mod_name; | ||
786 | |||
771 | mutex_unlock(&mem_ctls_mutex); | 787 | mutex_unlock(&mem_ctls_mutex); |
772 | return 0; | 788 | return 0; |
773 | 789 | ||
@@ -776,7 +792,7 @@ fail1: | |||
776 | 792 | ||
777 | fail0: | 793 | fail0: |
778 | mutex_unlock(&mem_ctls_mutex); | 794 | mutex_unlock(&mem_ctls_mutex); |
779 | return 1; | 795 | return ret; |
780 | } | 796 | } |
781 | EXPORT_SYMBOL_GPL(edac_mc_add_mc); | 797 | EXPORT_SYMBOL_GPL(edac_mc_add_mc); |
782 | 798 | ||
@@ -802,7 +818,8 @@ struct mem_ctl_info *edac_mc_del_mc(struct device *dev) | |||
802 | return NULL; | 818 | return NULL; |
803 | } | 819 | } |
804 | 820 | ||
805 | del_mc_from_global_list(mci); | 821 | if (!del_mc_from_global_list(mci)) |
822 | edac_mc_owner = NULL; | ||
806 | mutex_unlock(&mem_ctls_mutex); | 823 | mutex_unlock(&mem_ctls_mutex); |
807 | 824 | ||
808 | /* flush workq processes */ | 825 | /* flush workq processes */ |