aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJames Morse <james.morse@arm.com>2019-01-29 13:48:51 -0500
committerRafael J. Wysocki <rafael.j.wysocki@intel.com>2019-02-07 17:10:45 -0500
commit3b880cbe4df5dd78a2b2279dbe16db9d193412ca (patch)
tree936e9ff81c2627217f03733ec9f7c8ee93b6568e
parentd44f1b8dd7e66d80cc4205809e5ace866bd851da (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.c34
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 */
120static DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi); 120static DEFINE_SPINLOCK(ghes_notify_lock_irq);
121static DEFINE_SPINLOCK(ghes_ioremap_lock_irq);
122 121
123static struct gen_pool *ghes_estatus_pool; 122static struct gen_pool *ghes_estatus_pool;
124static unsigned long ghes_estatus_pool_size_request; 123static 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)
729static void ghes_poll_func(struct timer_list *t) 723static 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)
738static irqreturn_t ghes_irq_func(int irq, void *data) 735static 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 */
913int ghes_notify_sea(void) 916int 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
918static void ghes_sea_add(struct ghes *ghes) 928static void ghes_sea_add(struct ghes *ghes)
@@ -945,13 +955,16 @@ static LIST_HEAD(ghes_nmi);
945 955
946static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) 956static 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