From patchwork Sun Nov 2 06:04:33 2014 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Liu X-Patchwork-Id: 5210831 Return-Path: X-Original-To: patchwork-linux-pm@patchwork.kernel.org Delivered-To: patchwork-parsemail@patchwork1.web.kernel.org Received: from mail.kernel.org (mail.kernel.org [198.145.19.201]) by patchwork1.web.kernel.org (Postfix) with ESMTP id 890DF9F295 for ; Sun, 2 Nov 2014 06:04:22 +0000 (UTC) Received: from mail.kernel.org (localhost [127.0.0.1]) by mail.kernel.org (Postfix) with ESMTP id 5BB87200C1 for ; Sun, 2 Nov 2014 06:04:21 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id E7DC7200CC for ; Sun, 2 Nov 2014 06:04:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751094AbaKBGED (ORCPT ); Sun, 2 Nov 2014 01:04:03 -0500 Received: from mga02.intel.com ([134.134.136.20]:55616 "EHLO mga02.intel.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752703AbaKBGD6 (ORCPT ); Sun, 2 Nov 2014 01:03:58 -0500 Received: from orsmga001.jf.intel.com ([10.7.209.18]) by orsmga101.jf.intel.com with ESMTP; 01 Nov 2014 23:03:55 -0700 X-ExtLoop1: 1 X-IronPort-AV: E=Sophos;i="5.07,295,1413270000"; d="scan'208";a="600464719" Received: from gerry-dev.bj.intel.com ([10.238.158.52]) by orsmga001.jf.intel.com with ESMTP; 01 Nov 2014 23:03:51 -0700 From: Jiang Liu To: Benjamin Herrenschmidt , Thomas Gleixner , Ingo Molnar , "H. Peter Anvin" , "Rafael J. Wysocki" , Bjorn Helgaas , Randy Dunlap , Yinghai Lu , Borislav Petkov , Len Brown , Pavel Machek , x86@kernel.org Cc: Jiang Liu , Konrad Rzeszutek Wilk , Andrew Morton , Tony Luck , Joerg Roedel , Greg Kroah-Hartman , linux-kernel@vger.kernel.org, linux-pci@vger.kernel.org, linux-acpi@vger.kernel.org, linux-pm@vger.kernel.org Subject: [Patch v8 18/18] x86, irq, ACPI: Implement ACPI driver to support IOAPIC hotplug Date: Sun, 2 Nov 2014 14:04:33 +0800 Message-Id: <1414908273-7552-19-git-send-email-jiang.liu@linux.intel.com> X-Mailer: git-send-email 1.7.10.4 In-Reply-To: <1414908273-7552-1-git-send-email-jiang.liu@linux.intel.com> References: <1414908273-7552-1-git-send-email-jiang.liu@linux.intel.com> Sender: linux-pm-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-pm@vger.kernel.org X-Spam-Status: No, score=-7.5 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 Enable support of IOAPIC hotplug by: 1) reintroducing ACPI based IOAPIC driver 2) enhance pci_root driver to hook hotplug events The ACPI IOAPIC driver is always enabled if all of ACPI, PCI and IOAPIC are enabled. Signed-off-by: Jiang Liu --- arch/x86/kernel/acpi/boot.c | 12 +-- drivers/acpi/Kconfig | 6 ++ drivers/acpi/Makefile | 1 + drivers/acpi/internal.h | 7 ++ drivers/acpi/ioapic.c | 235 +++++++++++++++++++++++++++++++++++++++++++ drivers/acpi/pci_root.c | 3 + 6 files changed, 258 insertions(+), 6 deletions(-) create mode 100644 drivers/acpi/ioapic.c diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c index 0e18705cee1e..32e47f22593c 100644 --- a/arch/x86/kernel/acpi/boot.c +++ b/arch/x86/kernel/acpi/boot.c @@ -802,9 +802,9 @@ int acpi_register_ioapic(acpi_handle handle, u64 phys_addr, u32 gsi_base) ioapic_id = (int)uid; } - down_write(&acpi_ioapic_rwsem); + mutex_lock(&acpi_ioapic_lock); ret = mp_register_ioapic(ioapic_id, phys_addr, gsi_base, &cfg); - up_write(&acpi_ioapic_rwsem); + mutex_unlock(&acpi_ioapic_lock); #endif return ret; @@ -816,9 +816,9 @@ int acpi_unregister_ioapic(acpi_handle handle, u32 gsi_base) int ret = -ENOSYS; #ifdef CONFIG_ACPI_HOTPLUG_IOAPIC - down_write(&acpi_ioapic_rwsem); + mutex_lock(&acpi_ioapic_lock); ret = mp_unregister_ioapic(gsi_base); - up_write(&acpi_ioapic_rwsem); + mutex_unlock(&acpi_ioapic_lock); #endif return ret; @@ -839,9 +839,9 @@ int acpi_ioapic_registered(acpi_handle handle, u32 gsi_base) int ret = 0; #ifdef CONFIG_ACPI_HOTPLUG_IOAPIC - down_read(&acpi_ioapic_rwsem); + mutex_lock(&acpi_ioapic_lock); ret = mp_ioapic_registered(gsi_base); - up_read(&acpi_ioapic_rwsem); + mutex_unlock(&acpi_ioapic_lock); #endif return ret; diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index b23fe37f67c0..127bc2fc43be 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -315,6 +315,12 @@ config ACPI_HOTPLUG_MEMORY To compile this driver as a module, choose M here: the module will be called acpi_memhotplug. +config ACPI_HOTPLUG_IOAPIC + bool + depends on PCI + depends on X86_IO_APIC + default y + config ACPI_SBS tristate "Smart Battery System" depends on X86 diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile index c3b2fcb729f3..3b283a232f7a 100644 --- a/drivers/acpi/Makefile +++ b/drivers/acpi/Makefile @@ -69,6 +69,7 @@ obj-$(CONFIG_ACPI_PROCESSOR) += processor.o obj-y += container.o obj-$(CONFIG_ACPI_THERMAL) += thermal.o obj-y += acpi_memhotplug.o +obj-$(CONFIG_ACPI_HOTPLUG_IOAPIC) += ioapic.o obj-$(CONFIG_ACPI_BATTERY) += battery.o obj-$(CONFIG_ACPI_SBS) += sbshc.o obj-$(CONFIG_ACPI_SBS) += sbs.o diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 447f6d679b29..73efdcb8d8ad 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -35,6 +35,13 @@ void acpi_int340x_thermal_init(void); int acpi_sysfs_init(void); void acpi_container_init(void); void acpi_memory_hotplug_init(void); +#ifdef CONFIG_ACPI_HOTPLUG_IOAPIC +int acpi_ioapic_add(struct acpi_pci_root *root); +int acpi_ioapic_remove(struct acpi_pci_root *root); +#else +static inline int acpi_ioapic_add(struct acpi_pci_root *root) { return 0; } +static inline int acpi_ioapic_remove(struct acpi_pci_root *root) { return 0; } +#endif #ifdef CONFIG_ACPI_DOCK void register_dock_dependent_device(struct acpi_device *adev, acpi_handle dshandle); diff --git a/drivers/acpi/ioapic.c b/drivers/acpi/ioapic.c new file mode 100644 index 000000000000..9a631a61e270 --- /dev/null +++ b/drivers/acpi/ioapic.c @@ -0,0 +1,235 @@ +/* + * IOAPIC/IOxAPIC/IOSAPIC driver + * + * Copyright (C) 2009 Fujitsu Limited. + * (c) Copyright 2009 Hewlett-Packard Development Company, L.P. + * + * Copyright (C) 2014 Intel Corporation + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * Based on original drivers/pci/ioapic.c + * Yinghai Lu + * Jiang Liu + */ + +/* + * This driver manages I/O APICs added by hotplug after boot. + * We try to claim all I/O APIC devices, but those present at boot were + * registered when we parsed the ACPI MADT. + */ + +#define pr_fmt(fmt) "ACPI : IOAPIC: " fmt + +#include +#include +#include +#include + +struct acpi_pci_ioapic { + acpi_handle root_handle; + acpi_handle handle; + u32 gsi_base; + struct resource res; + struct pci_dev *pdev; + struct list_head list; +}; + +static LIST_HEAD(ioapic_list); +static DEFINE_MUTEX(ioapic_list_lock); + +static acpi_status setup_res(struct acpi_resource *acpi_res, void *data) +{ + struct resource *res = data; + + memset(res, 0, sizeof(*res)); + if (acpi_dev_resource_memory(acpi_res, res)) { + res->flags &= IORESOURCE_MEM; + if (res->flags) + return AE_OK; + } else if (acpi_dev_resource_address_space(acpi_res, res)) { + struct acpi_resource_address64 addr; + + res->flags &= IORESOURCE_MEM; + if (res->flags && + ACPI_SUCCESS(acpi_resource_to_address64(acpi_res, &addr)) && + addr.info.mem.caching != ACPI_PREFETCHABLE_MEMORY) { + res->start += addr.translation_offset; + res->end += addr.translation_offset; + return AE_OK; + } + } + res->flags = 0; + + return AE_OK; +} + +static bool acpi_is_ioapic(acpi_handle handle, char **type) +{ + acpi_status status; + struct acpi_device_info *info; + char *hid = NULL; + bool match = false; + + if (!acpi_has_method(handle, "_GSB")) + return false; + + status = acpi_get_object_info(handle, &info); + if (ACPI_SUCCESS(status)) { + if (info->valid & ACPI_VALID_HID) + hid = info->hardware_id.string; + if (hid) { + if (strcmp(hid, "ACPI0009") == 0) { + *type = "IOxAPIC"; + match = true; + } else if (strcmp(hid, "ACPI000A") == 0) { + *type = "IOAPIC"; + match = true; + } + } + kfree(info); + } + + return match; +} + +static acpi_status handle_ioapic_add(acpi_handle handle, u32 lvl, + void *context, void **rv) +{ + acpi_status status; + unsigned long long gsi_base; + struct acpi_pci_ioapic *ioapic; + struct pci_dev *dev = NULL; + struct resource *res = NULL; + char *type = NULL; + + if (!acpi_is_ioapic(handle, &type)) + return AE_OK; + + mutex_lock(&ioapic_list_lock); + list_for_each_entry(ioapic, &ioapic_list, list) + if (ioapic->handle == handle) { + mutex_unlock(&ioapic_list_lock); + return AE_OK; + } + + status = acpi_evaluate_integer(handle, "_GSB", NULL, &gsi_base); + if (ACPI_FAILURE(status)) { + acpi_handle_warn(handle, "failed to evaluate _GSB method\n"); + goto exit; + } + + ioapic = kzalloc(sizeof(*ioapic), GFP_KERNEL); + if (!ioapic) { + pr_err("cannot allocate memory for new IOAPIC\n"); + goto exit; + } else { + ioapic->root_handle = (acpi_handle)context; + ioapic->handle = handle; + ioapic->gsi_base = (u32)gsi_base; + ioapic->res.flags = IORESOURCE_UNSET; + } + + if (acpi_ioapic_registered(handle, (u32)gsi_base)) + goto done; + + dev = acpi_get_pci_dev(handle); + if (dev && pci_resource_len(dev, 0)) { + if (pci_enable_device(dev) < 0) + goto exit_put; + pci_set_master(dev); + if (pci_request_region(dev, 0, type)) + goto exit_disable; + res = &dev->resource[0]; + ioapic->pdev = dev; + } else { + pci_dev_put(dev); + dev = NULL; + + res = &ioapic->res; + acpi_walk_resources(handle, METHOD_NAME__CRS, setup_res, res); + if (res->flags == IORESOURCE_UNSET) { + acpi_handle_warn(handle, "failed to get resource\n"); + goto exit_free; + } else if (request_resource(&iomem_resource, res)) { + acpi_handle_warn(handle, "failed to insert resource\n"); + goto exit_free; + } + } + + if (acpi_register_ioapic(handle, res->start, (u32)gsi_base)) { + acpi_handle_warn(handle, "failed to register IOAPIC\n"); + goto exit_release; + } +done: + list_add(&ioapic->list, &ioapic_list); + mutex_unlock(&ioapic_list_lock); + + if (dev) + dev_info(&dev->dev, "%s at %pR, GSI %u\n", + type, res, (u32)gsi_base); + else + acpi_handle_info(handle, "%s at %pR, GSI %u\n", + type, res, (u32)gsi_base); + + return AE_OK; + +exit_release: + if (dev) + pci_release_region(dev, 0); + else + release_resource(res); +exit_disable: + if (dev) + pci_disable_device(dev); +exit_put: + if (dev) + pci_dev_put(dev); +exit_free: + kfree(ioapic); +exit: + mutex_unlock(&ioapic_list_lock); + *(acpi_status *)rv = AE_ERROR; + return AE_OK; +} + +int acpi_ioapic_add(struct acpi_pci_root *root) +{ + acpi_status status, retval = AE_OK; + + status = acpi_walk_namespace(ACPI_TYPE_DEVICE, root->device->handle, + UINT_MAX, handle_ioapic_add, NULL, + root->device->handle, (void **)&retval); + + return ACPI_SUCCESS(status) && ACPI_SUCCESS(retval) ? 0 : -ENODEV; +} + +int acpi_ioapic_remove(struct acpi_pci_root *root) +{ + int retval = 0; + struct acpi_pci_ioapic *ioapic, *tmp; + + mutex_lock(&ioapic_list_lock); + list_for_each_entry_safe(ioapic, tmp, &ioapic_list, list) { + if (root->device->handle != ioapic->root_handle) + continue; + + if (acpi_unregister_ioapic(ioapic->handle, ioapic->gsi_base)) + retval = -EBUSY; + + if (ioapic->pdev) { + pci_release_region(ioapic->pdev, 0); + pci_disable_device(ioapic->pdev); + pci_dev_put(ioapic->pdev); + } else if (ioapic->res.flags != IORESOURCE_UNSET) { + release_resource(&ioapic->res); + } + list_del(&ioapic->list); + kfree(ioapic); + } + mutex_unlock(&ioapic_list_lock); + + return retval; +} diff --git a/drivers/acpi/pci_root.c b/drivers/acpi/pci_root.c index cd4de7e038ea..f3f77689bafc 100644 --- a/drivers/acpi/pci_root.c +++ b/drivers/acpi/pci_root.c @@ -614,6 +614,7 @@ static int acpi_pci_root_add(struct acpi_device *device, if (system_state != SYSTEM_BOOTING) { pcibios_resource_survey_bus(root->bus); pci_assign_unassigned_root_bus_resources(root->bus); + acpi_ioapic_add(root); } pci_lock_rescan_remove(); @@ -634,6 +635,8 @@ static void acpi_pci_root_remove(struct acpi_device *device) pci_stop_root_bus(root->bus); + WARN_ON(acpi_ioapic_remove(root)); + device_set_run_wake(root->bus->bridge, false); pci_acpi_remove_bus_pm_notifier(device);