diff options
| author | James Morse <james.morse@arm.com> | 2019-01-29 13:48:51 -0500 |
|---|---|---|
| committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2019-02-07 17:10:45 -0500 |
| commit | 3b880cbe4df5dd78a2b2279dbe16db9d193412ca (patch) | |
| tree | 936e9ff81c2627217f03733ec9f7c8ee93b6568e | |
| parent | d44f1b8dd7e66d80cc4205809e5ace866bd851da (diff) | |
ACPI / APEI: Move locking to the notification helper
ghes_copy_tofrom_phys() takes different locks depending on in_nmi().
This doesn't work if there are multiple NMI-like notifications, that
can interrupt each other.
Now that NOTIFY_SEA is always called in the same context, move the
lock-taking to the notification helper. The helper will always know
which lock to take. This avoids ghes_copy_tofrom_phys() taking a guess
based on in_nmi().
This splits NOTIFY_NMI and NOTIFY_SEA to use different locks. All
the other notifications use ghes_proc(), and are called in process
or IRQ context. Move the spin_lock_irqsave() around their ghes_proc()
calls.
Signed-off-by: James Morse <james.morse@arm.com>
Reviewed-by: Borislav Petkov <bp@suse.de>
Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com>
| -rw-r--r-- | drivers/acpi/apei/ghes.c | 34 |
1 files changed, 25 insertions, 9 deletions
diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 679ab0f93ee8..c7dbdb915e7e 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c | |||
| @@ -114,11 +114,10 @@ static DEFINE_MUTEX(ghes_list_mutex); | |||
| 114 | * handler, but general ioremap can not be used in atomic context, so | 114 | * handler, but general ioremap can not be used in atomic context, so |
| 115 | * the fixmap is used instead. | 115 | * the fixmap is used instead. |
| 116 | * | 116 | * |
| 117 | * These 2 spinlocks are used to prevent the fixmap entries from being used | 117 | * This spinlock is used to prevent the fixmap entry from being used |
| 118 | * simultaneously. | 118 | * simultaneously. |
| 119 | */ | 119 | */ |
| 120 | static DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi); | 120 | static DEFINE_SPINLOCK(ghes_notify_lock_irq); |
| 121 | static DEFINE_SPINLOCK(ghes_ioremap_lock_irq); | ||
| 122 | 121 | ||
| 123 | static struct gen_pool *ghes_estatus_pool; | 122 | static struct gen_pool *ghes_estatus_pool; |
| 124 | static unsigned long ghes_estatus_pool_size_request; | 123 | static unsigned long ghes_estatus_pool_size_request; |
| @@ -287,7 +286,6 @@ static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, | |||
| 287 | int from_phys) | 286 | int from_phys) |
| 288 | { | 287 | { |
| 289 | void __iomem *vaddr; | 288 | void __iomem *vaddr; |
| 290 | unsigned long flags = 0; | ||
| 291 | int in_nmi = in_nmi(); | 289 | int in_nmi = in_nmi(); |
| 292 | u64 offset; | 290 | u64 offset; |
| 293 | u32 trunk; | 291 | u32 trunk; |
| @@ -295,10 +293,8 @@ static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, | |||
| 295 | while (len > 0) { | 293 | while (len > 0) { |
| 296 | offset = paddr - (paddr & PAGE_MASK); | 294 | offset = paddr - (paddr & PAGE_MASK); |
| 297 | if (in_nmi) { | 295 | if (in_nmi) { |
| 298 | raw_spin_lock(&ghes_ioremap_lock_nmi); | ||
| 299 | vaddr = ghes_ioremap_pfn_nmi(paddr >> PAGE_SHIFT); | 296 | vaddr = ghes_ioremap_pfn_nmi(paddr >> PAGE_SHIFT); |
| 300 | } else { | 297 | } else { |
| 301 | spin_lock_irqsave(&ghes_ioremap_lock_irq, flags); | ||
| 302 | vaddr = ghes_ioremap_pfn_irq(paddr >> PAGE_SHIFT); | 298 | vaddr = ghes_ioremap_pfn_irq(paddr >> PAGE_SHIFT); |
| 303 | } | 299 | } |
| 304 | trunk = PAGE_SIZE - offset; | 300 | trunk = PAGE_SIZE - offset; |
| @@ -312,10 +308,8 @@ static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, | |||
| 312 | buffer += trunk; | 308 | buffer += trunk; |
| 313 | if (in_nmi) { | 309 | if (in_nmi) { |
| 314 | ghes_iounmap_nmi(); | 310 | ghes_iounmap_nmi(); |
| 315 | raw_spin_unlock(&ghes_ioremap_lock_nmi); | ||
| 316 | } else { | 311 | } else { |
| 317 | ghes_iounmap_irq(); | 312 | ghes_iounmap_irq(); |
| 318 | spin_unlock_irqrestore(&ghes_ioremap_lock_irq, flags); | ||
| 319 | } | 313 | } |
| 320 | } | 314 | } |
| 321 | } | 315 | } |
| @@ -729,8 +723,11 @@ static void ghes_add_timer(struct ghes *ghes) | |||
| 729 | static void ghes_poll_func(struct timer_list *t) | 723 | static void ghes_poll_func(struct timer_list *t) |
| 730 | { | 724 | { |
| 731 | struct ghes *ghes = from_timer(ghes, t, timer); | 725 | struct ghes *ghes = from_timer(ghes, t, timer); |
| 726 | unsigned long flags; | ||
| 732 | 727 | ||
| 728 | spin_lock_irqsave(&ghes_notify_lock_irq, flags); | ||
| 733 | ghes_proc(ghes); | 729 | ghes_proc(ghes); |
| 730 | spin_unlock_irqrestore(&ghes_notify_lock_irq, flags); | ||
| 734 | if (!(ghes->flags & GHES_EXITING)) | 731 | if (!(ghes->flags & GHES_EXITING)) |
| 735 | ghes_add_timer(ghes); | 732 | ghes_add_timer(ghes); |
| 736 | } | 733 | } |
| @@ -738,9 +735,12 @@ static void ghes_poll_func(struct timer_list *t) | |||
| 738 | static irqreturn_t ghes_irq_func(int irq, void *data) | 735 | static irqreturn_t ghes_irq_func(int irq, void *data) |
| 739 | { | 736 | { |
| 740 | struct ghes *ghes = data; | 737 | struct ghes *ghes = data; |
| 738 | unsigned long flags; | ||
| 741 | int rc; | 739 | int rc; |
| 742 | 740 | ||
| 741 | spin_lock_irqsave(&ghes_notify_lock_irq, flags); | ||
| 743 | rc = ghes_proc(ghes); | 742 | rc = ghes_proc(ghes); |
| 743 | spin_unlock_irqrestore(&ghes_notify_lock_irq, flags); | ||
| 744 | if (rc) | 744 | if (rc) |
| 745 | return IRQ_NONE; | 745 | return IRQ_NONE; |
| 746 | 746 | ||
| @@ -751,14 +751,17 @@ static int ghes_notify_hed(struct notifier_block *this, unsigned long event, | |||
| 751 | void *data) | 751 | void *data) |
| 752 | { | 752 | { |
| 753 | struct ghes *ghes; | 753 | struct ghes *ghes; |
| 754 | unsigned long flags; | ||
| 754 | int ret = NOTIFY_DONE; | 755 | int ret = NOTIFY_DONE; |
| 755 | 756 | ||
| 757 | spin_lock_irqsave(&ghes_notify_lock_irq, flags); | ||
| 756 | rcu_read_lock(); | 758 | rcu_read_lock(); |
| 757 | list_for_each_entry_rcu(ghes, &ghes_hed, list) { | 759 | list_for_each_entry_rcu(ghes, &ghes_hed, list) { |
| 758 | if (!ghes_proc(ghes)) | 760 | if (!ghes_proc(ghes)) |
| 759 | ret = NOTIFY_OK; | 761 | ret = NOTIFY_OK; |
| 760 | } | 762 | } |
| 761 | rcu_read_unlock(); | 763 | rcu_read_unlock(); |
| 764 | spin_unlock_irqrestore(&ghes_notify_lock_irq, flags); | ||
| 762 | 765 | ||
| 763 | return ret; | 766 | return ret; |
| 764 | } | 767 | } |
| @@ -912,7 +915,14 @@ static LIST_HEAD(ghes_sea); | |||
| 912 | */ | 915 | */ |
| 913 | int ghes_notify_sea(void) | 916 | int ghes_notify_sea(void) |
| 914 | { | 917 | { |
| 915 | return ghes_in_nmi_spool_from_list(&ghes_sea); | 918 | static DEFINE_RAW_SPINLOCK(ghes_notify_lock_sea); |
| 919 | int rv; | ||
| 920 | |||
| 921 | raw_spin_lock(&ghes_notify_lock_sea); | ||
| 922 | rv = ghes_in_nmi_spool_from_list(&ghes_sea); | ||
| 923 | raw_spin_unlock(&ghes_notify_lock_sea); | ||
| 924 | |||
| 925 | return rv; | ||
| 916 | } | 926 | } |
| 917 | 927 | ||
| 918 | static void ghes_sea_add(struct ghes *ghes) | 928 | static void ghes_sea_add(struct ghes *ghes) |
| @@ -945,13 +955,16 @@ static LIST_HEAD(ghes_nmi); | |||
| 945 | 955 | ||
| 946 | static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) | 956 | static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) |
| 947 | { | 957 | { |
| 958 | static DEFINE_RAW_SPINLOCK(ghes_notify_lock_nmi); | ||
| 948 | int ret = NMI_DONE; | 959 | int ret = NMI_DONE; |
| 949 | 960 | ||
| 950 | if (!atomic_add_unless(&ghes_in_nmi, 1, 1)) | 961 | if (!atomic_add_unless(&ghes_in_nmi, 1, 1)) |
| 951 | return ret; | 962 | return ret; |
| 952 | 963 | ||
| 964 | raw_spin_lock(&ghes_notify_lock_nmi); | ||
| 953 | if (!ghes_in_nmi_spool_from_list(&ghes_nmi)) | 965 | if (!ghes_in_nmi_spool_from_list(&ghes_nmi)) |
| 954 | ret = NMI_HANDLED; | 966 | ret = NMI_HANDLED; |
| 967 | raw_spin_unlock(&ghes_notify_lock_nmi); | ||
| 955 | 968 | ||
| 956 | atomic_dec(&ghes_in_nmi); | 969 | atomic_dec(&ghes_in_nmi); |
| 957 | return ret; | 970 | return ret; |
| @@ -993,6 +1006,7 @@ static int ghes_probe(struct platform_device *ghes_dev) | |||
| 993 | { | 1006 | { |
| 994 | struct acpi_hest_generic *generic; | 1007 | struct acpi_hest_generic *generic; |
| 995 | struct ghes *ghes = NULL; | 1008 | struct ghes *ghes = NULL; |
| 1009 | unsigned long flags; | ||
| 996 | 1010 | ||
| 997 | int rc = -EINVAL; | 1011 | int rc = -EINVAL; |
| 998 | 1012 | ||
| @@ -1095,7 +1109,9 @@ static int ghes_probe(struct platform_device *ghes_dev) | |||
| 1095 | ghes_edac_register(ghes, &ghes_dev->dev); | 1109 | ghes_edac_register(ghes, &ghes_dev->dev); |
| 1096 | 1110 | ||
| 1097 | /* Handle any pending errors right away */ | 1111 | /* Handle any pending errors right away */ |
| 1112 | spin_lock_irqsave(&ghes_notify_lock_irq, flags); | ||
| 1098 | ghes_proc(ghes); | 1113 | ghes_proc(ghes); |
| 1114 | spin_unlock_irqrestore(&ghes_notify_lock_irq, flags); | ||
| 1099 | 1115 | ||
| 1100 | return 0; | 1116 | return 0; |
| 1101 | 1117 | ||
