From patchwork Fri Apr 27 15:35:05 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: James Morse X-Patchwork-Id: 10369319 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 97963601BE for ; Fri, 27 Apr 2018 15:44:10 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 846C329428 for ; Fri, 27 Apr 2018 15:44:10 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 78A342942A; Fri, 27 Apr 2018 15:44:10 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-2.9 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,MAILING_LIST_MULTI autolearn=unavailable version=3.3.1 Received: from bombadil.infradead.org (bombadil.infradead.org [198.137.202.133]) (using TLSv1.2 with cipher AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by mail.wl.linuxfoundation.org (Postfix) with ESMTPS id EF85729428 for ; Fri, 27 Apr 2018 15:44:09 +0000 (UTC) DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=lists.infradead.org; s=bombadil.20170209; h=Sender: Content-Transfer-Encoding:Content-Type:MIME-Version:Cc:List-Subscribe: List-Help:List-Post:List-Archive:List-Unsubscribe:List-Id:References: In-Reply-To:Message-Id:Date:Subject:To:From:Reply-To:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Owner; bh=ubgaSXTc4c4H+htZL0qAGpSFQSb/Ysw1dT/7K18CXFQ=; b=jhX01frJhJHxou1DdJ4lflJRql jYfkFMdp6vx00yc72XNcqTkZcmsCUUTM1a7zD03VnpJOWvLxeNkgk1oLSSosloI+LcPZBnAJpBAmk 8bMHcAfZu0H4OtmzkrQUtDJtZGPBl3cqL4zOE4PVcIvwt3z5fzWQfkSgu7HAu4azTTA3QCZRzfC41 W7MHDA/eqShJP7NELZ7JWTvZDD7AJUsBNsTS4Bt+DgX9jydAKi4B5bOfhfhFkWlAFS8YvX9PdCgrp ijGn021KRjkd8t+Ntgq7G/VHHjvbAkwOdRv9eaGZ/nFFPhvcTob3Gc7SZHEN8zXvd43VcemWKWfDZ Q7uOIQPw==; Received: from localhost ([127.0.0.1] helo=bombadil.infradead.org) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1fC5XX-0003CV-2r; Fri, 27 Apr 2018 15:44:03 +0000 Received: from usa-sjc-mx-foss1.foss.arm.com ([217.140.101.70] helo=foss.arm.com) by bombadil.infradead.org with esmtp (Exim 4.90_1 #2 (Red Hat Linux)) id 1fC5SP-0007Jv-NZ for linux-arm-kernel@lists.infradead.org; Fri, 27 Apr 2018 15:38:48 +0000 Received: from usa-sjc-imap-foss1.foss.arm.com (unknown [10.72.51.249]) by usa-sjc-mx-foss1.foss.arm.com (Postfix) with ESMTP id A998815AD; Fri, 27 Apr 2018 08:38:45 -0700 (PDT) Received: from melchizedek.cambridge.arm.com (melchizedek.cambridge.arm.com [10.1.207.55]) by usa-sjc-imap-foss1.foss.arm.com (Postfix) with ESMTPA id 9635E3F487; Fri, 27 Apr 2018 08:38:42 -0700 (PDT) From: James Morse To: linux-acpi@vger.kernel.org Subject: [PATCH v3 07/12] ACPI / APEI: Make the nmi_fixmap_idx per-ghes to allow multiple in_nmi() users Date: Fri, 27 Apr 2018 16:35:05 +0100 Message-Id: <20180427153510.5799-8-james.morse@arm.com> X-Mailer: git-send-email 2.16.2 In-Reply-To: <20180427153510.5799-1-james.morse@arm.com> References: <20180427153510.5799-1-james.morse@arm.com> X-CRM114-Version: 20100106-BlameMichelson ( TRE 0.8.0 (BSD) ) MR-646709E3 X-CRM114-CacheID: sfid-20180427_083845_899891_83883C60 X-CRM114-Status: GOOD ( 20.15 ) X-BeenThere: linux-arm-kernel@lists.infradead.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: jonathan.zhang@cavium.com, Rafael Wysocki , Tony Luck , Christoffer Dall , Punit Agrawal , Xie XiuQi , Marc Zyngier , Catalin Marinas , Tyler Baicar , Will Deacon , Dongjiu Geng , linux-mm@kvack.org, Borislav Petkov , James Morse , Naoya Horiguchi , kvmarm@lists.cs.columbia.edu, linux-arm-kernel@lists.infradead.org, Len Brown MIME-Version: 1.0 Sender: "linux-arm-kernel" Errors-To: linux-arm-kernel-bounces+patchwork-linux-arm=patchwork.kernel.org@lists.infradead.org X-Virus-Scanned: ClamAV using ClamSMTP Arm64 has multiple NMI-like notifications, but ghes.c only has one in_nmi() path, risking deadlock if one NMI-like notification can interrupt another. To support this we need a fixmap entry and lock for each notification type. But ghes_probe() attempts to process each struct ghes at probe time, to ensure any error that was notified before ghes_probe() was called has been done, and the buffer released (and maybe acknowledge to firmware) so that future errors can be delivered. This means NMI-like notifications need two fixmap entries and locks, one for the ghes_probe() time call, and another for the actual NMI that could interrupt ghes_probe(). Split this single path up by adding an NMI fixmap idx and lock into the struct ghes. Any notification that can be called as an NMI can use these to separate its resources from any other notification it may interrupt. The majority of notifications occur in IRQ context, so unless its called in_nmi(), ghes_copy_tofrom_phys() will use the FIX_APEI_GHES_IRQ fixmap entry and the ghes_fixmap_lock_irq lock. This allows NMI-notifications to be processed by ghes_probe(), and then taken as an NMI. The double-underscore version of fix_to_virt() is used because the index to be mapped can't be tested against the end of the enum at compile time. Signed-off-by: James Morse --- Changes since v1: * Fixed for ghes_proc() always calling every notification in process context. Now only NMI-like notifications need an additional fixmap-slot/lock. drivers/acpi/apei/ghes.c | 70 +++++++++++++++++------------------------------- include/acpi/ghes.h | 4 +++ 2 files changed, 28 insertions(+), 46 deletions(-) diff --git a/drivers/acpi/apei/ghes.c b/drivers/acpi/apei/ghes.c index 1859f27c37ff..48d9eb55ebb8 100644 --- a/drivers/acpi/apei/ghes.c +++ b/drivers/acpi/apei/ghes.c @@ -117,12 +117,9 @@ static DEFINE_MUTEX(ghes_list_mutex); * from BIOS to Linux can be determined only in NMI, IRQ or timer * handler, but general ioremap can not be used in atomic context, so * the fixmap is used instead. - * - * These 2 spinlocks are used to prevent the fixmap entries from being used - * simultaneously. */ -static DEFINE_RAW_SPINLOCK(ghes_ioremap_lock_nmi); -static DEFINE_SPINLOCK(ghes_ioremap_lock_irq); +static DEFINE_RAW_SPINLOCK(ghes_fixmap_lock_nmi); +static DEFINE_SPINLOCK(ghes_fixmap_lock_irq); static struct gen_pool *ghes_estatus_pool; static unsigned long ghes_estatus_pool_size_request; @@ -132,38 +129,16 @@ static atomic_t ghes_estatus_cache_alloced; static int ghes_panic_timeout __read_mostly = 30; -static void __iomem *ghes_ioremap_pfn_nmi(u64 pfn) -{ - phys_addr_t paddr; - pgprot_t prot; - - paddr = pfn << PAGE_SHIFT; - prot = arch_apei_get_mem_attribute(paddr); - __set_fixmap(FIX_APEI_GHES_NMI, paddr, prot); - - return (void __iomem *) fix_to_virt(FIX_APEI_GHES_NMI); -} - -static void __iomem *ghes_ioremap_pfn_irq(u64 pfn) +static void __iomem *ghes_fixmap_pfn(int fixmap_idx, u64 pfn) { phys_addr_t paddr; pgprot_t prot; paddr = pfn << PAGE_SHIFT; prot = arch_apei_get_mem_attribute(paddr); - __set_fixmap(FIX_APEI_GHES_IRQ, paddr, prot); + __set_fixmap(fixmap_idx, paddr, prot); - return (void __iomem *) fix_to_virt(FIX_APEI_GHES_IRQ); -} - -static void ghes_iounmap_nmi(void) -{ - clear_fixmap(FIX_APEI_GHES_NMI); -} - -static void ghes_iounmap_irq(void) -{ - clear_fixmap(FIX_APEI_GHES_IRQ); + return (void __iomem *) __fix_to_virt(fixmap_idx); } static int ghes_estatus_pool_init(void) @@ -291,10 +266,11 @@ static inline int ghes_severity(int severity) } } -static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, - int from_phys) +static void ghes_copy_tofrom_phys(struct ghes *ghes, void *buffer, u64 paddr, + u32 len, int from_phys) { void __iomem *vaddr; + int fixmap_idx = FIX_APEI_GHES_IRQ; unsigned long flags = 0; int in_nmi = in_nmi(); u64 offset; @@ -303,12 +279,12 @@ static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, while (len > 0) { offset = paddr - (paddr & PAGE_MASK); if (in_nmi) { - raw_spin_lock(&ghes_ioremap_lock_nmi); - vaddr = ghes_ioremap_pfn_nmi(paddr >> PAGE_SHIFT); + raw_spin_lock(ghes->nmi_fixmap_lock); + fixmap_idx = ghes->nmi_fixmap_idx; } else { - spin_lock_irqsave(&ghes_ioremap_lock_irq, flags); - vaddr = ghes_ioremap_pfn_irq(paddr >> PAGE_SHIFT); + spin_lock_irqsave(&ghes_fixmap_lock_irq, flags); } + vaddr = ghes_fixmap_pfn(fixmap_idx, paddr >> PAGE_SHIFT); trunk = PAGE_SIZE - offset; trunk = min(trunk, len); if (from_phys) @@ -318,13 +294,11 @@ static void ghes_copy_tofrom_phys(void *buffer, u64 paddr, u32 len, len -= trunk; paddr += trunk; buffer += trunk; - if (in_nmi) { - ghes_iounmap_nmi(); - raw_spin_unlock(&ghes_ioremap_lock_nmi); - } else { - ghes_iounmap_irq(); - spin_unlock_irqrestore(&ghes_ioremap_lock_irq, flags); - } + clear_fixmap(fixmap_idx); + if (in_nmi) + raw_spin_unlock(ghes->nmi_fixmap_lock); + else + spin_unlock_irqrestore(&ghes_fixmap_lock_irq, flags); } } @@ -346,7 +320,7 @@ static int ghes_read_estatus(struct ghes *ghes, int silent) if (!buf_paddr) return -ENOENT; - ghes_copy_tofrom_phys(ghes->estatus, buf_paddr, + ghes_copy_tofrom_phys(ghes, ghes->estatus, buf_paddr, sizeof(*ghes->estatus), 1); if (!ghes->estatus->block_status) return -ENOENT; @@ -362,7 +336,7 @@ static int ghes_read_estatus(struct ghes *ghes, int silent) goto err_read_block; if (cper_estatus_check_header(ghes->estatus)) goto err_read_block; - ghes_copy_tofrom_phys(ghes->estatus + 1, + ghes_copy_tofrom_phys(ghes, ghes->estatus + 1, buf_paddr + sizeof(*ghes->estatus), len - sizeof(*ghes->estatus), 1); if (cper_estatus_check(ghes->estatus)) @@ -381,7 +355,7 @@ static void ghes_clear_estatus(struct ghes *ghes) ghes->estatus->block_status = 0; if (!(ghes->flags & GHES_TO_CLEAR)) return; - ghes_copy_tofrom_phys(ghes->estatus, ghes->buffer_paddr, + ghes_copy_tofrom_phys(ghes, ghes->estatus, ghes->buffer_paddr, sizeof(ghes->estatus->block_status), 0); ghes->flags &= ~GHES_TO_CLEAR; } @@ -986,6 +960,8 @@ int ghes_notify_sea(void) static void ghes_sea_add(struct ghes *ghes) { + ghes->nmi_fixmap_lock = &ghes_fixmap_lock_nmi; + ghes->nmi_fixmap_idx = FIX_APEI_GHES_NMI; ghes_estatus_queue_grow_pool(ghes); mutex_lock(&ghes_list_mutex); @@ -1032,6 +1008,8 @@ static int ghes_notify_nmi(unsigned int cmd, struct pt_regs *regs) static void ghes_nmi_add(struct ghes *ghes) { + ghes->nmi_fixmap_lock = &ghes_fixmap_lock_nmi; + ghes->nmi_fixmap_idx = FIX_APEI_GHES_NMI; ghes_estatus_queue_grow_pool(ghes); mutex_lock(&ghes_list_mutex); diff --git a/include/acpi/ghes.h b/include/acpi/ghes.h index 8feb0c866ee0..9c6a92730699 100644 --- a/include/acpi/ghes.h +++ b/include/acpi/ghes.h @@ -29,6 +29,10 @@ struct ghes { struct timer_list timer; unsigned int irq; }; + + /* If this ghes can be called in NMI contet, these must be populated. */ + raw_spinlock_t *nmi_fixmap_lock; + int nmi_fixmap_idx; }; struct ghes_estatus_node {