From patchwork Fri Aug 17 14:37:48 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Hari Vyas X-Patchwork-Id: 10568935 X-Patchwork-Delegate: bhelgaas@google.com Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 080E8139A for ; Fri, 17 Aug 2018 14:38:05 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id EC5B028CB7 for ; Fri, 17 Aug 2018 14:38:04 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id DE7C32BBE2; Fri, 17 Aug 2018 14:38:04 +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=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham 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 5CAC728CB7 for ; Fri, 17 Aug 2018 14:38:04 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727434AbeHQRlj (ORCPT ); Fri, 17 Aug 2018 13:41:39 -0400 Received: from mail-qk0-f194.google.com ([209.85.220.194]:36622 "EHLO mail-qk0-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726544AbeHQRlj (ORCPT ); Fri, 17 Aug 2018 13:41:39 -0400 Received: by mail-qk0-f194.google.com with SMTP id 93-v6so5042811qks.3 for ; Fri, 17 Aug 2018 07:38:02 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=broadcom.com; s=google; h=from:to:cc:subject:date:message-id; bh=lFEjvn+dQ5v//dNiYnyq7ggRPEG6K8JBxPq4c+lVGQo=; b=UBeycLNNJSuXmvACYHATxmSUE3xxRIMOvWywdUeVfC9P9NI5r8uxqO/emKTcC8Whh8 sxTOJ5rgVl1dmrKOzc8fkQfNcZlHuxNk7AXdMsaEZZtptfmj26zvGVEpCec4Qs4zEQuw xUOSogUp+eG03YCuHSeGmJCN6kn1o5wAnZbuA= X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id; bh=lFEjvn+dQ5v//dNiYnyq7ggRPEG6K8JBxPq4c+lVGQo=; b=WeEPeEwVYhFQEEr1e6UBBGB0JRN7Oca1rf2jj1pG5P/acD40p7QTuNjMweDDuYJrlD vE1MLshWBvW8wywVeY6JEdddXXtAkJoWEJhKYVhOQ8nieDDNuuCBcUvOY/7x4k78npjC aalhmmsiihe9KJ1ilwgUR/Ln5Q6aWKQJpJfJa7Cu5ggzH4T+83NkSt0PoZzhkXFpkvMR Ig4uI0p/3Lg+KOopEC179ZsbAsoyrAtjnfPZBBWOQqqAvfYo66RNJueItGWQMi5S6ccv 5gm3PNpId5bu4gDdjxw4TucjRCTD9fcsjYkJEtLz7HrrHjYZ04Tl5bLl5qOBQS4UXShJ RvHg== X-Gm-Message-State: AOUpUlHt8Vw5ChufOyr9a1Bnlbg97diBXC+c1ASn3bj88SgA/o8ms+G9 HLYI08b6At/5Up/rzRt6RGvWUA== X-Google-Smtp-Source: AA+uWPzyTVujnu9VkpdfaPqm6dgu5nlHRCKlqfIVrzf/hX2pnqjMuucAUNNJ4wG0VN1ZFeP4tbXX2A== X-Received: by 2002:a37:27ca:: with SMTP id n193-v6mr5469238qkn.444.1534516682262; Fri, 17 Aug 2018 07:38:02 -0700 (PDT) Received: from hariv-server.dhcp.broadcom.net ([192.19.234.250]) by smtp.gmail.com with ESMTPSA id d8-v6sm1112165qkg.91.2018.08.17.07.37.59 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Fri, 17 Aug 2018 07:38:01 -0700 (PDT) From: Hari Vyas To: bhelgaas@google.com Cc: linux-pci@vger.kernel.org, Hari Vyas Subject: [PATCH] PCI: fix of read-write config operation in SMP environment Date: Fri, 17 Aug 2018 20:07:48 +0530 Message-Id: <1534516668-1206-1-git-send-email-hari.vyas@broadcom.com> X-Mailer: git-send-email 1.9.1 Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP Read modify write operations performed on same pci configuration registers which have bit fields may not work properly in SMP environment. Let us assume one thread tries to set master bit and other thread memory bit of PCI_COMMAND register, result may be unpredictable. Better we introduce a new routine in struct pci_ops like (int (*modify_safe)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 old, u32 new); This routine may first read current value and apply diff. Signed-off-by: Hari Vyas --- drivers/pci/access.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/pci.c | 1 + drivers/pci/setup-res.c | 2 +- include/linux/pci.h | 4 ++++ 4 files changed, 67 insertions(+), 1 deletion(-) diff --git a/drivers/pci/access.c b/drivers/pci/access.c index a3ad2fe..c4cf99b 100644 --- a/drivers/pci/access.c +++ b/drivers/pci/access.c @@ -588,3 +588,64 @@ int pci_write_config_dword(const struct pci_dev *dev, int where, return pci_bus_write_config_dword(dev->bus, dev->devfn, where, val); } EXPORT_SYMBOL(pci_write_config_dword); + +#define PCI_OP_MODIFY(size, type, len) \ +int pci_bus_modify_config_##size \ + (struct pci_bus *bus, unsigned int devfn, int pos, \ + type rval, type wval) \ +{ \ + int res; \ + unsigned long flags; \ + u32 rdata = 0; \ + u32 wdata = 0; \ + u32 temp_data = 0; \ + if (PCI_##size##_BAD) return PCIBIOS_BAD_REGISTER_NUMBER; \ + pci_lock_config(flags); \ + if (bus->ops->modify) { \ + res = bus->ops->modify(bus, devfn, pos, len, \ + rval, wval); \ + } else { \ + temp_data = rval ^ wval; \ + res = bus->ops->read(bus, devfn, pos, len, &rdata); \ + wdata = rdata ^ temp_data; \ + res = bus->ops->write(bus, devfn, pos, len, wdata); \ + } \ + pci_unlock_config(flags); \ + return res; \ +} + +PCI_OP_MODIFY(byte, u8, 1) +PCI_OP_MODIFY(word, u16, 2) +PCI_OP_MODIFY(dword, u32, 4) +int pci_modify_config_byte(const struct pci_dev *dev, int where, + u8 rval, u8 wval) +{ + if (pci_dev_is_disconnected(dev)) { + return PCIBIOS_DEVICE_NOT_FOUND; + } + return pci_bus_modify_config_byte(dev->bus, dev->devfn, where, + rval, wval); +} +EXPORT_SYMBOL(pci_modify_config_byte); + +int pci_modify_config_word(const struct pci_dev *dev, int where, + u16 rval, u16 wval) +{ + if (pci_dev_is_disconnected(dev)) { + return PCIBIOS_DEVICE_NOT_FOUND; + } + return pci_bus_modify_config_word(dev->bus, dev->devfn, where, + rval, wval); +} +EXPORT_SYMBOL(pci_modify_config_word); + +int pci_modify_config_dword(const struct pci_dev *dev, int where, + u32 rval, u32 wval) +{ + if (pci_dev_is_disconnected(dev)) { + return PCIBIOS_DEVICE_NOT_FOUND; + } + return pci_bus_modify_config_dword(dev->bus, dev->devfn, where, + rval, wval); +} +EXPORT_SYMBOL(pci_modify_config_dword); diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 316496e..db1c132 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3712,6 +3712,7 @@ static void __pci_set_master(struct pci_dev *dev, bool enable) pci_dbg(dev, "%s bus mastering\n", enable ? "enabling" : "disabling"); pci_write_config_word(dev, PCI_COMMAND, cmd); + pci_modify_config_word(dev, PCI_COMMAND, old_cmd, cmd); } dev->is_busmaster = enable; } diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index d8ca40a..a2e6fa4 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -493,7 +493,7 @@ int pci_enable_resources(struct pci_dev *dev, int mask) if (cmd != old_cmd) { pci_info(dev, "enabling device (%04x -> %04x)\n", old_cmd, cmd); - pci_write_config_word(dev, PCI_COMMAND, cmd); + pci_modify_config_word(dev, PCI_COMMAND, old_cmd, cmd); } return 0; } diff --git a/include/linux/pci.h b/include/linux/pci.h index c133ccf..d948c4b 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -664,6 +664,7 @@ struct pci_ops { void __iomem *(*map_bus)(struct pci_bus *bus, unsigned int devfn, int where); int (*read)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 *val); int (*write)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 val); + int (*modify)(struct pci_bus *bus, unsigned int devfn, int where, int size, u32 rval, u32 wval); }; /* @@ -991,6 +992,9 @@ int pci_generic_config_write32(struct pci_bus *bus, unsigned int devfn, int pci_write_config_byte(const struct pci_dev *dev, int where, u8 val); int pci_write_config_word(const struct pci_dev *dev, int where, u16 val); int pci_write_config_dword(const struct pci_dev *dev, int where, u32 val); +int pci_modify_config_byte(const struct pci_dev *dev, int where, u8 rval, u8 wval); +int pci_modify_config_word(const struct pci_dev *dev, int where, u16 rval, u16 wval); +int pci_modify_config_dword(const struct pci_dev *dev, int where, u32 rval, u32 wval); int pcie_capability_read_word(struct pci_dev *dev, int pos, u16 *val); int pcie_capability_read_dword(struct pci_dev *dev, int pos, u32 *val);