From patchwork Wed Oct 4 22:11:57 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Bjorn Helgaas X-Patchwork-Id: 9985759 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 F259A60586 for ; Wed, 4 Oct 2017 22:12:05 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id E4A4728C2F for ; Wed, 4 Oct 2017 22:12:05 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id D90DB28C33; Wed, 4 Oct 2017 22:12:05 +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=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 3B4D328C2F for ; Wed, 4 Oct 2017 22:12:03 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751267AbdJDWMB (ORCPT ); Wed, 4 Oct 2017 18:12:01 -0400 Received: from mail.kernel.org ([198.145.29.99]:41800 "EHLO mail.kernel.org" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1750951AbdJDWMB (ORCPT ); Wed, 4 Oct 2017 18:12:01 -0400 Received: from localhost (unknown [69.71.4.159]) (using TLSv1.2 with cipher DHE-RSA-AES128-SHA (128/128 bits)) (No client certificate requested) by mail.kernel.org (Postfix) with ESMTPSA id 2921221883; Wed, 4 Oct 2017 22:12:00 +0000 (UTC) DMARC-Filter: OpenDMARC Filter v1.3.2 mail.kernel.org 2921221883 Authentication-Results: mail.kernel.org; dmarc=none (p=none dis=none) header.from=kernel.org Authentication-Results: mail.kernel.org; spf=none smtp.mailfrom=helgaas@kernel.org Date: Wed, 4 Oct 2017 17:11:57 -0500 From: Bjorn Helgaas To: Qiang Zheng Cc: linux-pci@vger.kernel.org, linux-pm@vger.kernel.org, gabriele.paoloni@huawei.com, linuxarm@huawei.com Subject: Re: [PATCH V2] Bug fix for PME interrupt handler, add Root Status check Message-ID: <20171004221157.GL25517@bhelgaas-glaptop.roam.corp.google.com> References: <1506737526-94266-1-git-send-email-zhengqiang10@huawei.com> MIME-Version: 1.0 Content-Disposition: inline In-Reply-To: <1506737526-94266-1-git-send-email-zhengqiang10@huawei.com> User-Agent: Mutt/1.5.21 (2010-09-15) Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP On Sat, Sep 30, 2017 at 10:12:06AM +0800, Qiang Zheng wrote: > PCIe PME and hot plug share same interrupt number. In some special case, > Link down event cause hot plug interrupt, devices is not disconnected, > But read config will return 0xff. > > In that case, PME work function will run and not return Because > Root Status PME bit always 1 and can not be cleared. > > This patch add Root Status check in PME interrupt handler, > Just do same as pciehp isr Slot status check. > > Signed-off-by: Qiang Zheng > --- > drivers/pci/pcie/pme.c | 2 +- > 1 file changed, 1 insertion(+), 1 deletion(-) > > diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c > index fafdb16..2ff2e57 100644 > --- a/drivers/pci/pcie/pme.c > +++ b/drivers/pci/pcie/pme.c > @@ -273,7 +273,7 @@ static irqreturn_t pcie_pme_irq(int irq, void *context) > spin_lock_irqsave(&data->lock, flags); > pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta); > > - if (!(rtsta & PCI_EXP_RTSTA_PME)) { > + if (rtsta == U32_MAX || !(rtsta & PCI_EXP_RTSTA_PME)) { > spin_unlock_irqrestore(&data->lock, flags); > return IRQ_NONE; > } > I applied the patch below to pci/misc for v4.15. I think we need a similar test in pcie_pme_work_fn() itself. Without it, I think there's a race: if we get a legitimate PME, enter pcie_pme_work_fn(), and the link goes down, we could still get 0xffffffff the next time we read PCI_EXP_RTSTA. I also used "(u32) ~0" instead of U32_MAX because that's the style used elsewhere. It might be clunkier than necessary, but U32_MAX suggests a limit, and that's not really the concept here. I didn't add your reviewed-by, Rafael, since I made non-trivial changes to the patch. Bjorn commit 303029d6d55ba10a37c82c31a89eb5550cde77ec Author: Qiang Date: Thu Sep 28 11:54:34 2017 +0800 PCI/PME: Handle invalid data when reading Root Status PCIe PME and native hotplug share the same interrupt number, so hotplug interrupts are also processed by PME. In some cases, e.g., a Link Down interrupt, a device may be present but unreachable, so when we try to read its Root Status register, the read fails and we get all ones data (0xffffffff). Previously, we interpreted that data as PCI_EXP_RTSTA_PME being set, i.e., "some device has asserted PME," so we scheduled pcie_pme_work_fn(). This caused an infinite loop because pcie_pme_work_fn() tried to handle PME requests until PCI_EXP_RTSTA_PME is cleared, but with the link down, PCI_EXP_RTSTA_PME can't be cleared. Check for the invalid 0xffffffff data everywhere we read the Root Status register. 1469d17dd341 ("PCI: pciehp: Handle invalid data when reading from non-existent devices") added similar checks in the hotplug driver. Signed-off-by: Qiang Zheng [bhelgaas: changelog, also check in pcie_pme_work_fn(), use "~0" to follow other similar checks] Signed-off-by: Bjorn Helgaas diff --git a/drivers/pci/pcie/pme.c b/drivers/pci/pcie/pme.c index fafdb165dd2e..df290aa58dce 100644 --- a/drivers/pci/pcie/pme.c +++ b/drivers/pci/pcie/pme.c @@ -226,6 +226,9 @@ static void pcie_pme_work_fn(struct work_struct *work) break; pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta); + if (rtsta == (u32) ~0) + break; + if (rtsta & PCI_EXP_RTSTA_PME) { /* * Clear PME status of the port. If there are other @@ -273,7 +276,7 @@ static irqreturn_t pcie_pme_irq(int irq, void *context) spin_lock_irqsave(&data->lock, flags); pcie_capability_read_dword(port, PCI_EXP_RTSTA, &rtsta); - if (!(rtsta & PCI_EXP_RTSTA_PME)) { + if (rtsta == (u32) ~0 || !(rtsta & PCI_EXP_RTSTA_PME)) { spin_unlock_irqrestore(&data->lock, flags); return IRQ_NONE; }