From patchwork Wed Jul 3 15:16:16 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Haicheng Li X-Patchwork-Id: 2817901 X-Patchwork-Delegate: bhelgaas@google.com Return-Path: X-Original-To: patchwork-linux-pci@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork2.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork2.web.kernel.org (Postfix) with ESMTP id AF766BF4A1 for ; Wed, 3 Jul 2013 15:17:02 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 6DD9A201BF for ; Wed, 3 Jul 2013 15:17:01 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 112B620196 for ; Wed, 3 Jul 2013 15:17:00 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1754935Ab3GCPQ7 (ORCPT ); Wed, 3 Jul 2013 11:16:59 -0400 Received: from mga14.intel.com ([143.182.124.37]:10748 "EHLO mga14.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752797Ab3GCPQ6 (ORCPT ); Wed, 3 Jul 2013 11:16:58 -0400 Received: from fmsmga001.fm.intel.com ([10.253.24.23]) by azsmga102.ch.intel.com with ESMTP; 03 Jul 2013 08:16:57 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="4.87,988,1363158000"; d="scan'208";a="359989989" Received: from hli22-desktop.sh.intel.com ([10.239.72.171]) by fmsmga001.fm.intel.com with ESMTP; 03 Jul 2013 08:17:50 -0700 From: Haicheng Li To: linux-kernel@vger.kernel.org Cc: Haicheng Li , Haicheng Li , Bjorn Helgaas , linux-pci@vger.kernel.org Subject: [PATCH 1/3] PCI: Add hide_device support to pci subsystem. Date: Wed, 3 Jul 2013 23:16:16 +0800 Message-Id: <1372864578-6925-1-git-send-email-haicheng.li@linux.intel.com> X-Mailer: git-send-email 1.7.9.5 Sender: linux-pci-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pci@vger.kernel.org X-Spam-Status: No, score=-6.9 required=5.0 tests=BAYES_00, RCVD_IN_DNSWL_HI, RP_MATCHES_RCVD, UNPARSEABLE_RELAY autolearn=unavailable version=3.3.1 X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on mail.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP With more and more SOCs having pci device integrated into chip (e.g. Intel Atom series), it's useful to add an interface to cleanly hide pci devices from pci device scanning, which is because: 1. phone or tablet OEMs may choose disabling some pci device in the SOC, such as camera ISP in Intel Atom Z2580 chip, and etc. 2. if such disabled devices are not cleanly removed from pci device tree, then pci-core will still try to operate on the relative device control registers while S3 suspend and resume. 3. so hiding such devices from early begining will not only reduce the kernel boot time, but also optimize the latency of system suspend and resume. To hide pci devices, just pass such parameters to kernel at boot stage: pci=hide=[:]:.[; ...] Cc: Bjorn Helgaas Cc: linux-pci@vger.kernel.org Signed-off-by: Haicheng Li --- drivers/pci/pci.c | 2 ++ drivers/pci/pci.h | 2 ++ drivers/pci/probe.c | 94 +++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+) diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index a899d8b..e228d00 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -3915,6 +3915,8 @@ static int __init pci_setup(char *str) pcie_bus_config = PCIE_BUS_PEER2PEER; } else if (!strncmp(str, "pcie_scan_all", 13)) { pci_add_flags(PCI_SCAN_ALL_PCIE_DEVS); + } else if (!strncmp(str, "hide=", 5)) { + pci_hide_devices(str + 5, strlen(str + 5)); } else { printk(KERN_ERR "PCI: Unknown option `%s'\n", str); diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index d1182c4..783703a 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -317,4 +317,6 @@ static inline int pci_dev_specific_reset(struct pci_dev *dev, int probe) } #endif +void pci_hide_devices(const char *, size_t); + #endif /* DRIVERS_PCI_H */ diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index 70f10fa..4223ef3 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -11,6 +11,7 @@ #include #include #include +#include #include "pci.h" #define CARDBUS_LATENCY_TIMER 176 /* secondary latency timer */ @@ -1352,10 +1353,103 @@ void pci_device_add(struct pci_dev *dev, struct pci_bus *bus) pci_proc_attach_device(dev); } +static LIST_HEAD(pci_hidden_dev_list); +struct pci_hidden_dev { + struct list_head list; + int domain_nr; + int devfn; + unsigned char bus_nr; +}; + +static void do_pci_hide_device(int domain_nr, unsigned char bus_nr, int devfn) +{ + struct pci_hidden_dev *d; + + list_for_each_entry(d, &pci_hidden_dev_list, list) + if (devfn == d->devfn && bus_nr == d->bus_nr && + domain_nr == d->domain_nr) + return; + + d = kzalloc(sizeof(*d), GFP_KERNEL); + if (!d) + return; + + d->domain_nr = domain_nr; + d->bus_nr = bus_nr; + d->devfn = devfn; + + list_add_tail(&d->list, &pci_hidden_dev_list); +} + +static bool init_hidden = true; + +#define PCI_HIDE_PARAM_SIZE COMMAND_LINE_SIZE +static char pci_hide_param[PCI_HIDE_PARAM_SIZE] = {'\0'}; + +static void parse_hidden_devices(void) +{ + char *p = pci_hide_param; + int seg, bus, slot, func, devfn, count; + + /* parse pci.hide= command-line */ + while (*p != '\0') { + count = 0; + if (sscanf(p, "%x:%x:%x.%x%n", + &seg, &bus, &slot, &func, &count) != 4) { + seg = 0; + if (sscanf(p, "%x:%x.%x%n", + &bus, &slot, &func, &count) != 3) { + /* Invalid format */ + printk(KERN_ERR "PCI: Can't parse " + "hide parameter: %s\n", p); + break; + } + } + p += count; + devfn = (slot << 3) | func; + + do_pci_hide_device(seg, bus, devfn); + if (*p != ';') { + /* End of param or invalid format */ + break; + } + p++; + } + init_hidden = false; +} + +static bool is_device_hidden(struct pci_bus *bus, int devfn) +{ + struct pci_hidden_dev *d; + + if (unlikely(init_hidden)) + parse_hidden_devices(); + + list_for_each_entry(d, &pci_hidden_dev_list, list) + if (d->devfn == devfn && d->bus_nr == bus->number && + d->domain_nr == pci_domain_nr(bus)) + return true; + + return false; +} + +void pci_hide_devices(const char *str, size_t count) +{ + if (!str) + return; + if (count > PCI_HIDE_PARAM_SIZE - 1) + count = PCI_HIDE_PARAM_SIZE - 1; + strncpy(pci_hide_param, str, count); + pci_hide_param[count] = '\0'; +} + struct pci_dev *__ref pci_scan_single_device(struct pci_bus *bus, int devfn) { struct pci_dev *dev; + if (unlikely(is_device_hidden(bus, devfn))) + return NULL; + dev = pci_get_slot(bus, devfn); if (dev) { pci_dev_put(dev);