From patchwork Mon Mar 6 11:40:25 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Christian_K=C3=B6nig?= X-Patchwork-Id: 9605881 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.web.codeaurora.org (Postfix) with ESMTP id CC620602B4 for ; Mon, 6 Mar 2017 11:49:57 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id AD35C28249 for ; Mon, 6 Mar 2017 11:49:57 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id A15A128339; Mon, 6 Mar 2017 11:49:57 +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=-7.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID, DKIM_VALID_AU, FREEMAIL_FROM, 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 2FCEB28249 for ; Mon, 6 Mar 2017 11:49:57 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753609AbdCFLt4 (ORCPT ); Mon, 6 Mar 2017 06:49:56 -0500 Received: from pegasos-out.vodafone.de ([80.84.1.38]:48331 "EHLO pegasos-out.vodafone.de" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752935AbdCFLtz (ORCPT ); Mon, 6 Mar 2017 06:49:55 -0500 Received: from localhost (localhost.localdomain [127.0.0.1]) by pegasos-out.vodafone.de (Rohrpostix1 Daemon) with ESMTP id 68746261EE5; Mon, 6 Mar 2017 12:40:33 +0100 (CET) X-Virus-Scanned: amavisd-new at vodafone.de Authentication-Results: rohrpostix1.prod.vfnet.de (amavisd-new); dkim=pass header.i=@vodafone.de Received: from pegasos-out.vodafone.de ([127.0.0.1]) by localhost (rohrpostix1.prod.vfnet.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id fcwozTrWaiy9; Mon, 6 Mar 2017 12:40:31 +0100 (CET) Received: from smtp-01.vodafone.de (xsmail-dmz5.prod.vfnet.de [10.215.254.36]) by pegasos-out.vodafone.de (Rohrpostix1 Daemon) with ESMTP id 2FB5D261E4F; Mon, 6 Mar 2017 12:40:31 +0100 (CET) X-DKIM: OpenDKIM Filter v2.6.8 pegasos-out.vodafone.de 2FB5D261E4F DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=vodafone.de; s=mail; t=1488800431; bh=zQNISSuIh66vrAbffuRU0tEqzJqWhHGXT4GmDY63IN4=; h=From:To:Subject:Date:In-Reply-To:References; b=s/Li1jv6d4UwhRfD1JbUZp7DiYUaT2ON8cSbAjdISij3uwB2Tn+T3ekWlsi1Vq+ZN M03DDQ+70/U1oM5D9IBZCdsBRbVQ4qWd2cARZUe9q2oUH55MyvX/jRuQYTqSQHnq6Z CInhZCSEkQYdCoETOJoP1VWBphq1pNDzq7K0Qz+w= X-DKIM: OpenDKIM Filter v2.0.2 smtp-01.vodafone.de 98DE1E5399 X-Virus-Scanned: amavisd-new at vodafone.de Received: from smtp-01.vodafone.de ([127.0.0.1]) by localhost (xsmail-dmz5.prod.vfnet.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id dXdtDUuX-5nG; Mon, 6 Mar 2017 12:40:29 +0100 (CET) From: =?UTF-8?q?Christian=20K=C3=B6nig?= To: linux-pci@vger.kernel.org, dri-devel@lists.freedesktop.org, platform-driver-x86@vger.kernel.org, amd-gfx@lists.freedesktop.org, linux-kernel@vger.kernel.org Subject: [PATCH 2/5] PCI: add functionality for resizing resources Date: Mon, 6 Mar 2017 12:40:25 +0100 Message-Id: <1488800428-2854-2-git-send-email-deathsimple@vodafone.de> X-Mailer: git-send-email 2.7.4 In-Reply-To: <1488800428-2854-1-git-send-email-deathsimple@vodafone.de> References: <1488800428-2854-1-git-send-email-deathsimple@vodafone.de> MIME-Version: 1.0 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 From: Christian König This allows device drivers to request resizing their BARs. The function only tries to reprogram the windows of the bridge directly above the requesting device and only the BAR of the same type (usually mem, 64bit, prefetchable). This is done to make sure not to disturb other drivers by changing the BARs of their devices. If reprogramming the bridge BAR fails the old status is restored and -ENOSPC returned to the calling device driver. Signed-off-by: Christian König --- drivers/pci/setup-bus.c | 61 +++++++++++++++++++++++++++++++++++++++++++++++++ drivers/pci/setup-res.c | 45 ++++++++++++++++++++++++++++++++++++ include/linux/pci.h | 2 ++ 3 files changed, 108 insertions(+) diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index f30ca75..cfab2c7 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -1923,6 +1923,67 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) } EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources); +int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type) +{ + const unsigned long type_mask = IORESOURCE_IO | IORESOURCE_MEM | + IORESOURCE_PREFETCH | IORESOURCE_MEM_64; + + struct resource saved; + LIST_HEAD(add_list); + LIST_HEAD(fail_head); + struct pci_dev_resource *fail_res; + unsigned i; + int ret = 0; + + /* Release all children from the matching bridge resource */ + for (i = PCI_BRIDGE_RESOURCES; i < PCI_BRIDGE_RESOURCE_END; ++i) { + struct resource *res = &bridge->resource[i]; + + if ((res->flags & type_mask) != (type & type_mask)) + continue; + + saved = *res; + if (res->parent) { + release_child_resources(res); + release_resource(res); + } + res->start = 0; + res->end = 0; + break; + } + + if (i == PCI_BRIDGE_RESOURCE_END) + return -ENOENT; + + __pci_bus_size_bridges(bridge->subordinate, &add_list); + __pci_bridge_assign_resources(bridge, &add_list, &fail_head); + BUG_ON(!list_empty(&add_list)); + + /* restore size and flags */ + list_for_each_entry(fail_res, &fail_head, list) { + struct resource *res = fail_res->res; + + res->start = fail_res->start; + res->end = fail_res->end; + res->flags = fail_res->flags; + } + + /* Revert to the old configuration */ + if (!list_empty(&fail_head)) { + struct resource *res = &bridge->resource[i]; + + res->start = saved.start; + res->end = saved.end; + res->flags = saved.flags; + + pci_claim_resource(bridge, i); + ret = -ENOSPC; + } + + free_list(&fail_head); + return ret; +} + void pci_assign_unassigned_bus_resources(struct pci_bus *bus) { struct pci_dev *dev; diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index 9526e34..d03e6f1 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -363,6 +363,51 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, resource_size_t addsiz return 0; } +int pci_resize_resource(struct pci_dev *dev, int resno, int size) +{ + struct resource *res = dev->resource + resno; + u32 sizes = pci_rbar_get_sizes(dev, resno); + int old = pci_rbar_get_size(dev, resno); + u64 bytes = 1ULL << (size + 20); + int ret = 0; + + if (!sizes) + return -ENOTSUPP; + + if (!(sizes & (1 << size))) + return -EINVAL; + + if (old < 0) + return old; + + /* Make sure the resource isn't assigned before making it larger. */ + if (resource_size(res) < bytes && res->parent) { + release_resource(res); + res->end = resource_size(res) - 1; + res->start = 0; + if (resno < PCI_BRIDGE_RESOURCES) + pci_update_resource(dev, resno); + } + + if (pci_rbar_set_size(dev, resno, size)) + res->end = res->start + bytes - 1; + else + return -EIO; + + ret = pci_reassign_bridge_resources(dev->bus->self, res->flags); + if (ret) { + pci_rbar_set_size(dev, resno, old); + res->end = res->start + (1ULL << (old + 20)) - 1; + + pci_assign_unassigned_bus_resources(dev->bus); + pci_setup_bridge(dev->bus); + } + + pci_reenable_device(dev->bus->self); + return ret; +} +EXPORT_SYMBOL(pci_resize_resource); + int pci_enable_resources(struct pci_dev *dev, int mask) { u16 cmd, old_cmd; diff --git a/include/linux/pci.h b/include/linux/pci.h index 9f26ca4..c85d8d7 100644 --- a/include/linux/pci.h +++ b/include/linux/pci.h @@ -1055,6 +1055,7 @@ void pci_reset_bridge_secondary_bus(struct pci_dev *dev); void pci_update_resource(struct pci_dev *dev, int resno); int __must_check pci_assign_resource(struct pci_dev *dev, int i); int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align); +int __must_check pci_resize_resource(struct pci_dev *dev, int i, int size); int pci_select_bars(struct pci_dev *dev, unsigned long flags); bool pci_device_is_present(struct pci_dev *pdev); void pci_ignore_hotplug(struct pci_dev *dev); @@ -1135,6 +1136,7 @@ void pci_assign_unassigned_resources(void); void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge); void pci_assign_unassigned_bus_resources(struct pci_bus *bus); void pci_assign_unassigned_root_bus_resources(struct pci_bus *bus); +int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type); void pdev_enable_device(struct pci_dev *); int pci_enable_resources(struct pci_dev *, int mask); void pci_fixup_irqs(u8 (*)(struct pci_dev *, u8 *),