linux-kernel.vger.kernel.org archive mirror
 help / color / mirror / Atom feed
* [RFC PATCH v3 00/28] ACPI based system device hotplug framework
@ 2012-10-06 15:27 Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 01/28] ACPIHP: introduce a framework for ACPI based system device hotplug Jiang Liu
                   ` (28 more replies)
  0 siblings, 29 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

The patchset is based on "5f3d2f2e1a63679cf1c4a4210f2f1cc2f335bef6"
from git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
And you pull the changeset from git://github.com/jiangliu/linux.git acpihp

Modern high-end server may support advanced hotplug features for system
devices, including physical processor, memory board, IO extension board
and/or computer node. The ACPI specifications have provided standard
interfaces between firmware and OS to support device hotplug at runtime.
This patch series provide an ACPI based hotplug framework to support system
device hotplug at runtime, which will replace current existing ACPI device
driver based CPU/memory/CONTAINER hotplug mechanism.

The new ACPI system device hotplug framework has following design goals:
1) Provide a mechanism to detect hotplug slots by checking ACPI _EJ0 method,
ACPI PRCT (platform RAS capabilities table) and other platform specific
mechanisms.
2) Unify the way to enumerate ACPI based hotplug slots. All hotplug slots
will be enumerated by the enumeration driver, instead of by ACPI device
drivers.
3) Unify the way to handle ACPI hotplug events. All ACPI hotplug events
for system devices will be handled by a generic ACPI hotplug driver,
instead of handled by ACPI device drivers.
4) Solve dependencies among hotplug slots. You need first to remove the
memory device before removing a physical processor if a hotpluggable memory
device is connected to a hotpluggable physical processor.
5) Provide better error handling and recover.
6) Provide interface to cancel ongoing hotplug operations. It may take a
very long time to remove a memory device, so provide interface to cancel
the operation. This is important especially for memory hot-removal.
7) Support new RAS features, such as socket/memory migration.
8) Provide better user interfaces to the hotplug functionalities.
9) Trigger hotplug events/operations by software. This feature is useful
   for hardware fault management and power saving.

The new ACPI based hotplug framework is modelled after PCI hotplug
architecture, which uses a hotplug driver to manage hotplug operations
and different device drivers to manage individual devices.

The new framework contains three main components as follow:
1) ACPI system device hotplug slot enumerator, which enumerates all ACPI
   hotplug slots according to platform specific knowledges. And it also
   provides callbacks to control each slot, such as poweron, poweroff,
   get_status etc. Currently there are two built-in methods to detect
   hotplug slots.
1.a) Detecting hotplug slot by checking _EJ0 method.
1.b) Faking hotplug slot. Use "modprobe acpihp_slot fake_slot=0xnn",
     0x1 for CONTAINER, 0x2 for memory device, 0x4 for CPU and 0x8 PCI host
     bridge. The fake slot enumerator will treat each assigned ACPI device
     as a hotplug slot, so it could be used for testing on hardware platforms
     without hardware hotplug capabilities.
2) ACPI system device hotplug driver, which implements a platform indenpendent
   state machine for hotplug slots and also provides sysfs based user
   interfaces.
3) Enhanced ACPI device drivers for CONTAINER, CPU, Memroy and PCI host bridge.
   All logic to handle hotplug events has been moved from these ACPI device
   driver into the hotplug driver. Now these ACPI device dirvers only take
   resposibilities configure/unconfigure corresponding system devices when
   hotplug happens.

Now 1), 2) and part of 2) are ready, we still need to enhance mem_hotplug
and pci_root drivers to support new hotplug framework.

On my laptop with fake slot enumerator, we got:
root@cat:/sys/devices/LNXSYSTM:00/acpihp# ls
CPU00  CPU01  CPU02  CPU03  CPU04  CPU05  CPU06  CPU07  IOX00
root@cat:/sys/devices/LNXSYSTM:00/acpihp# cd CPU07/
root@cat:/sys/devices/LNXSYSTM:00/acpihp/CPU07# ls
capabilities  connect     device  object  state   subsystem  uevent
configure     dependency  enable  power   status  type

/* Hot-remove CPU07 */
root@cat:/sys/devices/LNXSYSTM:00/acpihp/CPU07# echo 0 > enable
/* Hot-add CPU07 */
root@cat:/sys/devices/LNXSYSTM:00/acpihp/CPU07# echo 1 > configure

V2->V3 changes:
1) Fix minor bugs and refine code
2) Implement fake slot enumerator for testing
3) Enhance ACPI container driver to support new hotplug framework
4) Enhance ACPI processor driver to support new hotplug framework
5) Resolve dependencies among slots by evaluating _EDL method.
TODO:
1) Enhance ACPI mem_hotplug driver to support new hotplug framework
2) Enhance ACPI pci_root driver to support new PCI host bridge hotplug
   based on Yinghai's work

V1->V2 changes:
1) implement the ACPI hotplug driver
2) enhance ACPI container driver to support new hotplug framework
TODO:
1) handle ACPI hotplug events in ACPI hotplug driver
2) handle dependencies among slots by evaluating ACPI _EDL
3) enhance ACPI processor, memory and PCI root drivers to support new
   hotplug framework
4) develop an ACPI driver for IOAPIC
5) thorough tests:)

Jiang Liu (28):
  ACPIHP: introduce a framework for ACPI based system device hotplug
  ACPIHP: ACPI system device hotplug slot enumerator driver
  ACPIHP: detect ACPI hotplug slots by checking ACPI _EJ0 method
  ACPIHP: implement a fake ACPI system device hotplug slot enumerator
  ACPI: introduce interfaces to manage ACPI device reference count
  ACPIHP: introduce interfaces to scan and walk ACPI devices connecting
    to a slot
  ACPIHP: use klist to manage ACPI devices connecting to a slot
  ACPIHP: add callbacks into acpi_device_ops to support new hotplug
    framework
  ACPIHP: provide interfaces to associate driver specific data with
    slots
  ACPIHP: implement utility functions to support system device hotplug
  ACPIHP: implement skeleton of ACPI system device hotplug driver
  ACPIHP: analyse dependencies among ACPI hotplug slots
  ACPIHP: provide interface to cancel inprogress hotplug operations
  ACPIHP: configure/unconfigure system devices connecting to a hotplug
    slot
  ACPIHP: implement the state machine for ACPI hotplug slots
  ACPIHP: implement sysfs interfaces for ACPI system device hotplug
  ACPIHP: block ACPI device driver unloading when executing hotplug
    operations
  ACPIHP/container: change ACPI container driver to support new hotplug
    framework
  ACPIHP/container: move container.c into drivers/acpi/hotplug
  ACPI/processor: remove dead code from processor_driver.c
  ACPIHP/processor: reorganize ACPI processor driver for new hotplug
    framework
  ACPIHP/processor: protect accesses to device->driver_data
  ACPIHP/processor: enhance processor driver to support new hotplug
    framework
  CPU: introduce busy flag to temporarily disable CPU online sysfs
    interface
  ACPIHP/processor: reject online/offline requests when doing processor
    hotplug
  ACPI/processor: save parsed APIC ID in processor driver data
    structure
  x86: simplify _acpi_map_lsapic()
  ACPI/processor: serialize call to acpi_map/unmap_lsapic

 arch/ia64/include/asm/cpu.h            |    2 +-
 arch/ia64/kernel/acpi.c                |   38 +-
 arch/ia64/kernel/topology.c            |   15 +-
 arch/x86/include/asm/cpu.h             |    2 +-
 arch/x86/include/asm/mpspec.h          |    2 +-
 arch/x86/kernel/acpi/boot.c            |   82 +---
 arch/x86/kernel/apic/apic.c            |    8 +-
 arch/x86/kernel/topology.c             |   10 +-
 drivers/acpi/Kconfig                   |   69 ++-
 drivers/acpi/Makefile                  |    3 +-
 drivers/acpi/bus.c                     |   22 +-
 drivers/acpi/container.c               |  296 -----------
 drivers/acpi/hotplug/Makefile          |   21 +
 drivers/acpi/hotplug/acpihp.h          |   37 ++
 drivers/acpi/hotplug/acpihp_drv.h      |  107 ++++
 drivers/acpi/hotplug/cancel.c          |  174 +++++++
 drivers/acpi/hotplug/configure.c       |  354 ++++++++++++++
 drivers/acpi/hotplug/container.c       |  135 +++++
 drivers/acpi/hotplug/core.c            |  842 ++++++++++++++++++++++++++++++++
 drivers/acpi/hotplug/dependency.c      |  249 ++++++++++
 drivers/acpi/hotplug/device.c          |  208 ++++++++
 drivers/acpi/hotplug/drv_main.c        |  351 +++++++++++++
 drivers/acpi/hotplug/slot.c            |  421 ++++++++++++++++
 drivers/acpi/hotplug/slot_ej0.c        |  153 ++++++
 drivers/acpi/hotplug/slot_fake.c       |  177 +++++++
 drivers/acpi/hotplug/state_machine.c   |  631 ++++++++++++++++++++++++
 drivers/acpi/hotplug/sysfs.c           |  245 ++++++++++
 drivers/acpi/internal.h                |    7 +
 drivers/acpi/processor_core.c          |   26 +-
 drivers/acpi/processor_driver.c        |  557 ++++++++++-----------
 drivers/acpi/scan.c                    |   17 +-
 drivers/base/cpu.c                     |   22 +
 drivers/gpu/drm/i915/intel_opregion.c  |    2 +
 drivers/gpu/drm/nouveau/nouveau_acpi.c |    1 +
 drivers/pci/hotplug/acpiphp_glue.c     |    9 +-
 drivers/pci/hotplug/acpiphp_ibm.c      |    5 +-
 drivers/pci/hotplug/sgi_hotplug.c      |    6 +-
 drivers/platform/x86/thinkpad_acpi.c   |    2 +
 drivers/pnp/pnpacpi/core.c             |   23 +-
 drivers/xen/cpu_hotplug.c              |    2 +-
 include/acpi/acpi_bus.h                |   21 +-
 include/acpi/acpi_hotplug.h            |  323 ++++++++++++
 include/acpi/container.h               |   12 -
 include/acpi/processor.h               |    3 +
 include/linux/acpi.h                   |    2 +-
 include/linux/cpu.h                    |    2 +
 46 files changed, 4923 insertions(+), 773 deletions(-)
 delete mode 100644 drivers/acpi/container.c
 create mode 100644 drivers/acpi/hotplug/Makefile
 create mode 100644 drivers/acpi/hotplug/acpihp.h
 create mode 100644 drivers/acpi/hotplug/acpihp_drv.h
 create mode 100644 drivers/acpi/hotplug/cancel.c
 create mode 100644 drivers/acpi/hotplug/configure.c
 create mode 100644 drivers/acpi/hotplug/container.c
 create mode 100644 drivers/acpi/hotplug/core.c
 create mode 100644 drivers/acpi/hotplug/dependency.c
 create mode 100644 drivers/acpi/hotplug/device.c
 create mode 100644 drivers/acpi/hotplug/drv_main.c
 create mode 100644 drivers/acpi/hotplug/slot.c
 create mode 100644 drivers/acpi/hotplug/slot_ej0.c
 create mode 100644 drivers/acpi/hotplug/slot_fake.c
 create mode 100644 drivers/acpi/hotplug/state_machine.c
 create mode 100644 drivers/acpi/hotplug/sysfs.c
 create mode 100644 include/acpi/acpi_hotplug.h
 delete mode 100644 include/acpi/container.h

-- 
1.7.9.5


^ permalink raw reply	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 01/28] ACPIHP: introduce a framework for ACPI based system device hotplug
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 02/28] ACPIHP: ACPI system device hotplug slot enumerator driver Jiang Liu
                   ` (27 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci, Gaohuai Han

Modern high-end server may support advanced hotplug features for system
devices, including physical processor, memory board, IO extension
board/box and/or computer node. The ACPI specifications have provided
standard interfaces between firmware and OS to support device hotplug
at runtime. This patch series provide an ACPI based hotplug framework
to support system device hotplug at runtime, which will replace current
existing ACPI device driver based CPU/memory/CONTAINER hotplug mechanism.

An ACPI hotplug slot is an abstraction of receptacles, where a group of
system devices could be connected to. The ACPI system device hotplug
framework includes two main components to manage hotplug slots:
1) A system device hotplug slot enumerator driver, which enumerates all
   hotplug slots in the system and provides platform specific methods
   to control those slots.
2) A system device hotplug driver, which is a platform independent
   driver to manage all hotplug slots created by the slot enumerator.
   The hotplug driver implements a state machine for hotplug slots and
   provides user interfaces to manage hotplug slots.

To get rid of dependengcy between enumerator and hotplug driver, common
code shared by them will be built into kernel. The common code provides
some helper routines and a device class named acpihp_slot_class. Class
acpihp_slot_class provides following default sysfs class properties:
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
Signed-off-by: Gaohuai Han <hangaohuai@huawei.com>
---
 drivers/acpi/Kconfig          |   13 +
 drivers/acpi/Makefile         |    2 +
 drivers/acpi/hotplug/Makefile |    6 +
 drivers/acpi/hotplug/acpihp.h |   32 +++
 drivers/acpi/hotplug/core.c   |  562 +++++++++++++++++++++++++++++++++++++++++
 include/acpi/acpi_hotplug.h   |  200 +++++++++++++++
 6 files changed, 815 insertions(+)
 create mode 100644 drivers/acpi/hotplug/Makefile
 create mode 100644 drivers/acpi/hotplug/acpihp.h
 create mode 100644 drivers/acpi/hotplug/core.c
 create mode 100644 include/acpi/acpi_hotplug.h

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 119d58d..01b87c7 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -321,6 +321,19 @@ config X86_PM_TIMER
 	  You should nearly always say Y here because many modern
 	  systems require this timer. 
 
+menuconfig ACPI_HOTPLUG
+	bool "ACPI Based System Device Hotplug (EXPERIMENTAL)"
+	depends on (X86 || IA64) && SYSFS && EXPERIMENTAL
+	default n
+	help
+	  System devices, including processor, memory and PCI host bridge etc,
+	  could be dynamically reconfigured at runtime through ACPI device
+	  configuration interfaces. This option enables a framework to
+	  dynamically reconfiguring system devices based ACPI specifications.
+
+	  If your hardware and firmware do not support dynamic reconfiguration
+	  of system devices at runtime, you need not to enable this option.
+
 config ACPI_CONTAINER
 	tristate "Container and Module Devices (EXPERIMENTAL)"
 	depends on EXPERIMENTAL
diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 47199e2..17bea6c 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -73,3 +73,5 @@ obj-$(CONFIG_ACPI_PROCESSOR_AGGREGATOR) += acpi_pad.o
 obj-$(CONFIG_ACPI_IPMI)		+= acpi_ipmi.o
 
 obj-$(CONFIG_ACPI_APEI)		+= apei/
+
+obj-$(CONFIG_ACPI_HOTPLUG)	+= hotplug/
diff --git a/drivers/acpi/hotplug/Makefile b/drivers/acpi/hotplug/Makefile
new file mode 100644
index 0000000..5e7790f
--- /dev/null
+++ b/drivers/acpi/hotplug/Makefile
@@ -0,0 +1,6 @@
+#
+# Makefile for ACPI based system device hotplug drivers
+#
+
+obj-$(CONFIG_ACPI_HOTPLUG)			+= acpihp.o
+acpihp-y					= core.o
diff --git a/drivers/acpi/hotplug/acpihp.h b/drivers/acpi/hotplug/acpihp.h
new file mode 100644
index 0000000..7467895
--- /dev/null
+++ b/drivers/acpi/hotplug/acpihp.h
@@ -0,0 +1,32 @@
+/*
+ * Copyright (C) 2012 Huawei Tech. Co., Ltd.
+ * Copyright (C) 2012 Jiang Liu <jiang.liu@huawei.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#ifndef	ACPIHP_INTERNAL_H
+#define	ACPIHP_INTERNAL_H
+#include <acpi/acpi.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_hotplug.h>
+
+extern struct acpi_device *acpi_root;
+
+#endif
diff --git a/drivers/acpi/hotplug/core.c b/drivers/acpi/hotplug/core.c
new file mode 100644
index 0000000..086f4e5
--- /dev/null
+++ b/drivers/acpi/hotplug/core.c
@@ -0,0 +1,562 @@
+/*
+ * Copyright (C) 2012 Huawei Tech. Co., Ltd.
+ * Copyright (C) 2012 Jiang Liu <jiang.liu@huawei.com>
+ * Copyright (C) 2012 Gaohuai Han <hangaohuai@huawei.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/types.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/bug.h>
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <linux/sem.h>
+#include <linux/version.h>
+#include <acpi/acpi.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_hotplug.h>
+#include "acpihp.h"
+
+#define to_acpihp_slot(d) container_of(d, struct acpihp_slot, dev)
+
+static DEFINE_MUTEX(acpihp_mutex);
+static int acpihp_class_count;
+static struct kset *acpihp_slot_kset;
+
+static char *acpihp_slot_names[ACPIHP_SLOT_TYPE_MAX] = {
+	[ACPIHP_SLOT_TYPE_UNKNOWN]	= "UNKNOWN",
+	[ACPIHP_SLOT_TYPE_COMMON]	= "SLOT",
+	[ACPIHP_SLOT_TYPE_NODE]		= "NODE",
+	[ACPIHP_SLOT_TYPE_SYSTEM_BOARD]	= "SB",
+	[ACPIHP_SLOT_TYPE_CPU]		= "CPU",
+	[ACPIHP_SLOT_TYPE_MEM]		= "MEM",
+	[ACPIHP_SLOT_TYPE_IOX]		= "IOX",
+};
+
+static char *acpihp_slot_descriptions[ACPIHP_SLOT_TYPE_MAX] = {
+	[ACPIHP_SLOT_TYPE_UNKNOWN]	= "unknown ACPI hotplug slot",
+	[ACPIHP_SLOT_TYPE_COMMON]	= "generic ACPI hotplug slot",
+	[ACPIHP_SLOT_TYPE_NODE]		= "ACPI hotplug slot for computer node",
+	[ACPIHP_SLOT_TYPE_SYSTEM_BOARD]	= "ACPI hotplug slot for system board",
+	[ACPIHP_SLOT_TYPE_CPU]		= "ACPI hotplug slot for processor",
+	[ACPIHP_SLOT_TYPE_MEM]		= "ACPI hotplug slot for memory",
+	[ACPIHP_SLOT_TYPE_IOX]		= "ACPI hotplug slot for IO extension",
+};
+
+static char *acpihp_slot_states[] = {
+	[ACPIHP_SLOT_STATE_UNKNOWN]		= "unknown",
+	[ACPIHP_SLOT_STATE_ABSENT]		= "absent",
+	[ACPIHP_SLOT_STATE_PRESENT]		= "present",
+	[ACPIHP_SLOT_STATE_POWERED]		= "powered",
+	[ACPIHP_SLOT_STATE_CONNECTED]		= "connected",
+	[ACPIHP_SLOT_STATE_CONFIGURED]		= "configured",
+	[ACPIHP_SLOT_STATE_POWERING_ON]		= "powering on",
+	[ACPIHP_SLOT_STATE_POWERING_OFF]	= "powering off",
+	[ACPIHP_SLOT_STATE_CONNECTING]		= "connecting",
+	[ACPIHP_SLOT_STATE_DISCONNECTING]	= "disconneting",
+	[ACPIHP_SLOT_STATE_CONFIGURING]		= "configuring",
+	[ACPIHP_SLOT_STATE_UNCONFIGURING]	= "unconfiguring",
+};
+
+static char *acpihp_slot_status[] = {
+	"ok",
+	"irremovable",
+	"fault",
+	"irremovable, fault",
+};
+
+static char *acpihp_dev_container_ids[] = {
+	"ACPI0004",
+	"PNP0A05",
+	"PNP0A06",
+	NULL
+};
+
+static char *acpihp_dev_cpu_ids[] = {
+	"ACPI0007",
+	"LNXCPU",
+	NULL
+};
+
+static char *acpihp_dev_mem_ids[] = {
+	"PNP0C80",
+	NULL
+};
+
+static char *acpihp_dev_pcihb_ids[] = {
+	"PNP0A03",
+	"PNP0A08",
+	NULL
+};
+
+static char *acpihp_dev_ioapic_ids[] = {
+	"ACPI0009",
+	"ACPI000A",
+	"ACPI000B",
+	NULL
+};
+
+static void acpihp_slot_release(struct device *dev)
+{
+	struct acpihp_slot *slot = to_acpihp_slot(dev);
+
+	kfree(slot);
+}
+
+struct acpihp_slot *acpihp_create_slot(acpi_handle handle, char *name)
+{
+	struct acpihp_slot *slot;
+
+	if (name && strlen(name) >= ACPIHP_SLOT_NAME_MAX_SIZE) {
+		ACPIHP_DEBUG("slot name '%s' is too big.\n", name);
+		return NULL;
+	}
+
+	slot = kzalloc(sizeof(*slot), GFP_KERNEL);
+	if (slot == NULL) {
+		ACPIHP_DEBUG("fails to allocate memory for slot device.\n");
+		return NULL;
+	}
+
+	slot->handle = handle;
+	INIT_LIST_HEAD(&slot->slot_list);
+	INIT_LIST_HEAD(&slot->drvdata_list);
+	if (name)
+		strncpy(slot->name, name, sizeof(slot->name) - 1);
+	mutex_init(&slot->slot_mutex);
+
+	slot->dev.class = &acpihp_slot_class;
+	device_initialize(&slot->dev);
+
+	return slot;
+}
+EXPORT_SYMBOL_GPL(acpihp_create_slot);
+
+int acpihp_register_slot(struct acpihp_slot *slot)
+{
+	int ret;
+
+	if (!slot || !slot->slot_ops)
+		return -EINVAL;
+
+	/* Use ACPI root device to host top level hotplug slots */
+	if (slot->parent)
+		slot->dev.parent = &slot->parent->dev;
+	else
+		slot->dev.parent = &acpi_root->dev;
+
+	ret = device_add(&slot->dev);
+	if (!ret) {
+		slot->flags |= ACPIHP_SLOT_FLAG_REGISTERED;
+		if (sysfs_create_link(&acpihp_slot_kset->kobj, &slot->dev.kobj,
+				      slot->name))
+			dev_warn(&slot->dev, "fails to create symlink.\n");
+	}
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(acpihp_register_slot);
+
+void acpihp_unregister_slot(struct acpihp_slot *slot)
+{
+	if (slot && (slot->flags & ACPIHP_SLOT_FLAG_REGISTERED)) {
+		sysfs_remove_link(&acpihp_slot_kset->kobj, slot->name);
+		device_del(&slot->dev);
+	}
+}
+EXPORT_SYMBOL_GPL(acpihp_unregister_slot);
+
+struct acpihp_slot *acpihp_slot_get(struct acpihp_slot *slot)
+{
+	if (slot)
+		get_device(&slot->dev);
+
+	return slot;
+}
+EXPORT_SYMBOL_GPL(acpihp_slot_get);
+
+void acpihp_slot_put(struct acpihp_slot *slot)
+{
+	if (slot)
+		put_device(&slot->dev);
+}
+EXPORT_SYMBOL_GPL(acpihp_slot_put);
+
+static void acpihp_slot_data_handler(acpi_handle handle, void *context)
+{
+	return;
+}
+
+acpi_status acpihp_mark_slot(acpi_handle handle, struct acpihp_slot *slot)
+{
+	acpi_status status;
+
+	mutex_lock(&acpihp_mutex);
+	status = acpi_attach_data(handle, &acpihp_slot_data_handler, slot);
+	mutex_unlock(&acpihp_mutex);
+
+	return status;
+}
+EXPORT_SYMBOL_GPL(acpihp_mark_slot);
+
+acpi_status acpihp_unmark_slot(acpi_handle handle)
+{
+	acpi_status result;
+
+	mutex_lock(&acpihp_mutex);
+	result = acpi_detach_data(handle, &acpihp_slot_data_handler);
+	if (result == AE_NOT_FOUND)
+		result = AE_OK;
+	BUG_ON(result != AE_OK);
+	mutex_unlock(&acpihp_mutex);
+
+	return result;
+}
+EXPORT_SYMBOL_GPL(acpihp_unmark_slot);
+
+bool acpihp_is_slot(acpi_handle handle)
+{
+	acpi_status result;
+	void *data = NULL;
+
+	result = acpi_get_data(handle, &acpihp_slot_data_handler, &data);
+	BUG_ON(result != AE_OK && result != AE_NOT_FOUND);
+
+	return (result == AE_OK);
+}
+EXPORT_SYMBOL_GPL(acpihp_is_slot);
+
+bool acpihp_get_slot(acpi_handle handle, struct acpihp_slot **slot)
+{
+	acpi_status result;
+	void *data = NULL;
+
+	mutex_lock(&acpihp_mutex);
+	result = acpi_get_data(handle, &acpihp_slot_data_handler, &data);
+	if (slot) {
+		if (result == AE_OK) {
+			*slot = data;
+			acpihp_slot_get(*slot);
+		} else
+			*slot = NULL;
+	}
+	mutex_unlock(&acpihp_mutex);
+
+	return (result == AE_OK);
+}
+EXPORT_SYMBOL_GPL(acpihp_get_slot);
+
+bool acpihp_dev_match_ids(struct acpi_device_info *infop, char **ids)
+{
+	int i, j;
+	struct acpica_device_id_list *cid_list;
+
+	if (infop == NULL || ids == NULL) {
+		ACPIHP_DEBUG("invalid parameters.\n");
+		return false;
+	}
+
+	if (infop->valid & ACPI_VALID_HID) {
+		for (i = 0; ids[i]; i++) {
+			if (strncmp(ids[i], infop->hardware_id.string,
+				    infop->hardware_id.length) == 0) {
+				return true;
+			}
+		}
+	}
+
+	if (!(infop->valid & ACPI_VALID_CID)) {
+		for (i = 0; ids[i]; i++) {
+			cid_list = &infop->compatible_id_list;
+			for (j = 0; j < cid_list->count; j++) {
+				if (strncmp(ids[i],
+					    cid_list->ids[j].string,
+					    cid_list->ids[j].length) == 0) {
+						return true;
+				}
+			}
+		}
+	}
+
+	return false;
+}
+EXPORT_SYMBOL_GPL(acpihp_dev_match_ids);
+
+int acpihp_dev_get_type(acpi_handle handle, enum acpihp_dev_type *type)
+{
+	struct acpi_device_info *infop = NULL;
+
+	if (handle == NULL || type == NULL) {
+		ACPIHP_DEBUG("invalid parameters.\n");
+		return -EINVAL;
+	}
+	if (ACPI_FAILURE(acpi_get_object_info(handle, &infop)))
+		return -ENODEV;
+
+	*type = ACPIHP_DEV_TYPE_UNKNOWN;
+	if (infop->type == ACPI_TYPE_PROCESSOR) {
+		*type = ACPIHP_DEV_TYPE_CPU;
+	} else if (infop->type == ACPI_TYPE_DEVICE) {
+		if (acpihp_dev_match_ids(infop, acpihp_dev_container_ids))
+			*type = ACPIHP_DEV_TYPE_CONTAINER;
+		else if (acpihp_dev_match_ids(infop, acpihp_dev_cpu_ids))
+			*type = ACPIHP_DEV_TYPE_CPU;
+		else if (acpihp_dev_match_ids(infop, acpihp_dev_mem_ids))
+			*type = ACPIHP_DEV_TYPE_MEM;
+		else if (acpihp_dev_match_ids(infop, acpihp_dev_pcihb_ids))
+			*type = ACPIHP_DEV_TYPE_HOST_BRIDGE;
+		else if (acpihp_dev_match_ids(infop, acpihp_dev_ioapic_ids))
+			*type = ACPIHP_DEV_TYPE_IOAPIC;
+		else if ((infop->valid & (ACPI_VALID_ADR | ACPI_VALID_HID |
+			 ACPI_VALID_CID)) == ACPI_VALID_ADR)
+			*type = ACPIHP_DEV_TYPE_MAX;
+	}
+	kfree(infop);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(acpihp_dev_get_type);
+
+char *acpihp_get_slot_type_name(enum acpihp_slot_type type)
+{
+	if (type < 0 || type >= ACPIHP_SLOT_TYPE_MAX) {
+		ACPIHP_DEBUG("invalid parameters.\n");
+		return "UNKNOWN";
+	} else {
+		return acpihp_slot_names[type];
+	}
+}
+EXPORT_SYMBOL_GPL(acpihp_get_slot_type_name);
+
+/*
+ * slot_ops should be valid during the life cycle of a slot, so no protection.
+ */
+acpi_status acpihp_slot_get_status(struct acpihp_slot *slot, u64 *status)
+{
+	acpi_status rc;
+
+	if (slot == NULL || status == NULL) {
+		ACPIHP_DEBUG("invalid parameters.\n");
+		return AE_BAD_PARAMETER;
+	} else if (slot->slot_ops == NULL) {
+		ACPIHP_SLOT_DEBUG(slot, "operation not supported.\n");
+		return AE_SUPPORT;
+	} else if (slot->slot_ops->get_status)
+		return slot->slot_ops->get_status(slot->handle, status);
+
+	rc = acpi_evaluate_integer(slot->handle, METHOD_NAME__UID,
+				   NULL, status);
+	if (rc == AE_NOT_FOUND) {
+		*status = ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED |
+			  ACPI_STA_DEVICE_FUNCTIONING;
+		rc = AE_OK;
+	}
+
+	return rc;
+}
+EXPORT_SYMBOL_GPL(acpihp_slot_get_status);
+
+acpi_status acpihp_slot_poweron(struct acpihp_slot *slot)
+{
+	if (slot == NULL) {
+		ACPIHP_DEBUG("invalid parameter.\n");
+		return AE_BAD_PARAMETER;
+	} else if (slot->slot_ops == NULL || slot->slot_ops->poweron == NULL) {
+		ACPIHP_SLOT_DEBUG(slot, "operation not supported.\n");
+		return AE_SUPPORT;
+	}
+
+	return slot->slot_ops->poweron(slot->handle);
+}
+EXPORT_SYMBOL_GPL(acpihp_slot_poweron);
+
+acpi_status acpihp_slot_poweroff(struct acpihp_slot *slot)
+{
+	if (slot == NULL) {
+		ACPIHP_DEBUG("invalid parameter.\n");
+		return AE_BAD_PARAMETER;
+	} else if (slot->slot_ops == NULL || slot->slot_ops->poweroff == NULL) {
+		ACPIHP_SLOT_DEBUG(slot, "operation not supported.\n");
+		return AE_SUPPORT;
+	}
+
+	return slot->slot_ops->poweroff(slot->handle);
+}
+EXPORT_SYMBOL_GPL(acpihp_slot_poweroff);
+
+/* SYSFS interfaces */
+static ssize_t acpihp_slot_object_show(struct device *d,
+		struct device_attribute *attr, char *buf)
+{
+	ssize_t sz;
+	struct acpihp_slot *slot = to_acpihp_slot(d);
+	struct acpi_buffer path = {ACPI_ALLOCATE_BUFFER, NULL};
+
+	sz = acpi_get_name(slot->handle, ACPI_FULL_PATHNAME, &path);
+	if (sz)
+		goto end;
+
+	sz = sprintf(buf, "%s\n", (char *)path.pointer);
+	kfree(path.pointer);
+end:
+	return sz;
+}
+
+static ssize_t acpihp_slot_type_show(struct device *d,
+		struct device_attribute *attr, char *buf)
+{
+	enum acpihp_slot_type type;
+	struct acpihp_slot *slot = to_acpihp_slot(d);
+
+	type = slot->type < 0 || slot->type >= ACPIHP_SLOT_TYPE_MAX ?
+		ACPIHP_SLOT_TYPE_UNKNOWN : slot->type;
+
+	return snprintf(buf, PAGE_SIZE, "%s: %s\n",
+			acpihp_get_slot_type_name(type),
+			acpihp_slot_descriptions[type]);
+}
+
+static ssize_t acpihp_slot_state_show(struct device *d,
+		struct device_attribute *attr, char *buf)
+{
+	enum acpihp_slot_state state;
+	acpi_status status;
+	unsigned long long sta;
+	struct acpihp_slot *slot = to_acpihp_slot(d);
+
+	state = slot->state < 0 || slot->state >= ACPIHP_SLOT_STATE_MAX ?
+		ACPIHP_SLOT_STATE_UNKNOWN : slot->state;
+
+	if ((ACPIHP_SLOT_STATE_ABSENT == state) ||
+	     (ACPIHP_SLOT_STATE_PRESENT == state)) {
+		status = acpihp_slot_get_status(slot, &sta);
+		if (ACPI_SUCCESS(status)) {
+			if ((sta & ACPI_STA_DEVICE_PRESENT) ==
+				ACPI_STA_DEVICE_PRESENT)
+				state = ACPIHP_SLOT_STATE_PRESENT;
+			else
+				state = ACPIHP_SLOT_STATE_ABSENT;
+		}
+	}
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", acpihp_slot_states[state]);
+}
+
+static ssize_t acpihp_slot_status_show(struct device *d,
+		struct device_attribute *attr, char *buf)
+{
+	struct acpihp_slot *slot = to_acpihp_slot(d);
+	u32 status = slot->flags & ACPIHP_SLOT_STATUS_MASK;
+
+	return snprintf(buf, PAGE_SIZE, "%s\n", acpihp_slot_status[status]);
+}
+
+static ssize_t acpihp_slot_capabilities_show(struct device *d,
+		struct device_attribute *attr, char *buf)
+{
+	ssize_t sz;
+	struct acpihp_slot *slot = to_acpihp_slot(d);
+	u32 cap = slot->capabilities;
+
+	sz = snprintf(buf, PAGE_SIZE, "%s%s%s%s%s%s",
+		(cap & ACPIHP_SLOT_CAP_ONLINE) ? "online," : "",
+		(cap & ACPIHP_SLOT_CAP_OFFLINE) ? "offline," : "",
+		(cap & ACPIHP_SLOT_CAP_POWERON) ? "poweron," : "",
+		(cap & ACPIHP_SLOT_CAP_POWEROFF) ? "poweroff," : "",
+		(cap & ACPIHP_SLOT_CAP_HOTPLUG) ? "hotplug," : "",
+		(cap & ACPIHP_SLOT_CAP_MIGRATE) ? "migrate," : "");
+	BUG_ON(sz == 0);
+	if (sz)
+		buf[--sz] = '\n';
+
+	return sz + 1;
+}
+
+struct device_attribute acpihp_slot_dev_attrs[] = {
+	__ATTR(object, S_IRUGO, acpihp_slot_object_show, NULL),
+	__ATTR(type, S_IRUGO, acpihp_slot_type_show, NULL),
+	__ATTR(state, S_IRUGO, acpihp_slot_state_show, NULL),
+	__ATTR(status, S_IRUGO, acpihp_slot_status_show, NULL),
+	__ATTR(capabilities, S_IRUGO, acpihp_slot_capabilities_show, NULL),
+	__ATTR_NULL
+};
+
+/* The device class to support ACPI hotplug slots. */
+struct class acpihp_slot_class = {
+	.name		= "acpihp",
+	.dev_release	= &acpihp_slot_release,
+	.dev_attrs	= acpihp_slot_dev_attrs,
+};
+EXPORT_SYMBOL_GPL(acpihp_slot_class);
+
+int acpihp_register_class(void)
+{
+	int retval = 0;
+	struct kset *acpi_bus_kset;
+
+	mutex_lock(&acpihp_mutex);
+	BUG_ON(acpihp_class_count < 0);
+	if (acpihp_class_count == 0) {
+		acpi_bus_kset = bus_get_kset(&acpi_bus_type);
+		acpihp_slot_kset = kset_create_and_add("acpihp", NULL,
+						       &acpi_bus_kset->kobj);
+		if (!acpihp_slot_kset) {
+			ACPIHP_DEBUG("fails to create kset.\n");
+			retval = -ENOMEM;
+			goto out_unlock;
+		}
+
+		retval = class_register(&acpihp_slot_class);
+		if (retval) {
+			ACPIHP_DEBUG("fails to register acpihp_slot_class.\n");
+			kset_unregister(acpihp_slot_kset);
+			goto out_unlock;
+		}
+	}
+	acpihp_class_count++;
+out_unlock:
+	mutex_unlock(&acpihp_mutex);
+
+	return retval;
+}
+EXPORT_SYMBOL_GPL(acpihp_register_class);
+
+void acpihp_unregister_class(void)
+{
+	mutex_lock(&acpihp_mutex);
+	BUG_ON(acpihp_class_count <= 0);
+	--acpihp_class_count;
+	if (acpihp_class_count == 0) {
+		class_unregister(&acpihp_slot_class);
+		kset_unregister(acpihp_slot_kset);
+		acpihp_slot_kset = NULL;
+	}
+	mutex_unlock(&acpihp_mutex);
+}
+EXPORT_SYMBOL_GPL(acpihp_unregister_class);
+
+#ifdef	DEBUG
+int acpihp_debug = 1;
+#else
+int acpihp_debug;
+#endif
+EXPORT_SYMBOL_GPL(acpihp_debug);
+module_param_named(debug, acpihp_debug, int, S_IRUGO | S_IWUSR);
diff --git a/include/acpi/acpi_hotplug.h b/include/acpi/acpi_hotplug.h
new file mode 100644
index 0000000..0b61a10
--- /dev/null
+++ b/include/acpi/acpi_hotplug.h
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2012 Huawei Tech. Co., Ltd.
+ * Copyright (C) 2012 Jiang Liu <jiang.liu@huawei.com>
+ * Copyright (C) 2012 Gaohuai Han <hangaohuai@huawei.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#ifndef	__ACPI_HOTPLUG_H__
+#define	__ACPI_HOTPLUG_H__
+#include <linux/types.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/klist.h>
+#include <acpi/acpi.h>
+#include <acpi/acpi_drivers.h>
+
+#ifdef	CONFIG_ACPI_HOTPLUG
+
+#define	ACPIHP_SLOT_NAME_MAX_SIZE		16
+
+/* Types of system devices supported by the ACPI hotplug framework. */
+enum acpihp_dev_type {
+	ACPIHP_DEV_TYPE_UNKNOWN = 0,	/* Unknown device type */
+	ACPIHP_DEV_TYPE_CONTAINER,	/* ACPI container device */
+	ACPIHP_DEV_TYPE_MEM,		/* Memory device */
+	ACPIHP_DEV_TYPE_CPU,		/* Logical CPU device */
+	ACPIHP_DEV_TYPE_HOST_BRIDGE,	/* PCI/PCIe host bridge */
+	ACPIHP_DEV_TYPE_IOAPIC,		/* IOAPIC/IOxAPIC/IOSAPIC */
+	ACPIHP_DEV_TYPE_MAX
+};
+
+/*
+ * ACPI hotplug slot is an abstraction of receptacles where a group of
+ * system devices could be attached, just like PCI slot in PCI hotplug.
+ */
+enum acpihp_slot_type {
+	ACPIHP_SLOT_TYPE_UNKNOWN = 0,	/* Unknown slot type */
+	ACPIHP_SLOT_TYPE_COMMON,	/* Generic hotplug slot */
+	ACPIHP_SLOT_TYPE_NODE,		/* Node hosts CPU, MEM & IO devices */
+	ACPIHP_SLOT_TYPE_SYSTEM_BOARD,	/* System board hosts CPU & MEM */
+	ACPIHP_SLOT_TYPE_CPU,		/* CPU board */
+	ACPIHP_SLOT_TYPE_MEM,		/* Memory board */
+	ACPIHP_SLOT_TYPE_IOX,		/* IO eXtension board */
+	ACPIHP_SLOT_TYPE_MAX
+};
+
+/*
+ * States of ACPI hotplug slot:
+ *                     (POWERON)      (CONNECT)      (CONFIGURE)
+ * [ABSENT] <-> [PRESENT] <-> [POWERED] <-> [CONNECTED] <-> [CONFIGURED]
+ *                     (POWEROFF)    (DISCONNECT)   (UNCONFIGURE)
+ */
+enum acpihp_slot_state {
+	ACPIHP_SLOT_STATE_UNKNOWN = 0,
+	ACPIHP_SLOT_STATE_ABSENT,	/* slot is empty. */
+	ACPIHP_SLOT_STATE_PRESENT,	/* slot is populated. */
+	ACPIHP_SLOT_STATE_POWERED,	/* attached devices are powered. */
+	ACPIHP_SLOT_STATE_CONNECTED,	/* ACPI device nodes created. */
+	ACPIHP_SLOT_STATE_CONFIGURED,	/* attached devices are configured. */
+	ACPIHP_SLOT_STATE_POWERING_ON,	/* powering devices on */
+	ACPIHP_SLOT_STATE_POWERING_OFF,	/* powering devices off */
+	ACPIHP_SLOT_STATE_CONNECTING,	/* creating ACPI device nodes */
+	ACPIHP_SLOT_STATE_DISCONNECTING,/* destroying ACPI device nodes */
+	ACPIHP_SLOT_STATE_CONFIGURING,	/* configuring devices */
+	ACPIHP_SLOT_STATE_UNCONFIGURING,/* unconfigure devices */
+	ACPIHP_SLOT_STATE_MAX
+};
+
+/* Devices attached to the slot can't be hot-removed. */
+#define	ACPIHP_SLOT_FLAG_IRREMOVABLE	0x1
+/* Devices attached to the slot have encountered serious problems. */
+#define	ACPIHP_SLOT_FLAG_FAULT		0x2
+#define	ACPIHP_SLOT_STATUS_MASK		0x3
+/* Devices attached to the slot have been registered. */
+#define	ACPIHP_SLOT_FLAG_REGISTERED	0x80000000
+
+/* ACPI hotplug slot capabilities. */
+#define	ACPIHP_SLOT_CAP_ONLINE		0x1  /* Logical online */
+#define	ACPIHP_SLOT_CAP_OFFLINE		0x2  /* Logical offline */
+#define	ACPIHP_SLOT_CAP_POWERON		0x4  /* Physical power on */
+#define	ACPIHP_SLOT_CAP_POWEROFF	0x8  /* Physical power off */
+#define	ACPIHP_SLOT_CAP_HOTPLUG		0x10 /* Physical hotplug */
+#define	ACPIHP_SLOT_CAP_MIGRATE		0x20 /* Device migration */
+
+struct acpihp_slot;
+
+/*
+ * Callbacks provided by the platform dependent hotplug slot driver, which
+ * will be used by the platform independent ACPI hotplug framework to manage
+ * ACPI hotplug slots.
+ */
+struct acpihp_slot_ops {
+	struct module *owner;
+	char *desc;
+	acpi_status (*init)(void);
+	void (*fini)(void);					/* optional */
+	acpi_status (*check)(acpi_handle handle);
+	acpi_status (*create)(struct acpihp_slot *slot);
+	void (*destroy)(struct acpihp_slot *slot);		/* optional */
+	acpi_status (*poweron)(struct acpihp_slot *slot);	/* optional */
+	acpi_status (*poweroff)(struct acpihp_slot *slot);	/* optional */
+	acpi_status (*get_status)(struct acpihp_slot *slot,
+				  u64 *status);			/* optional */
+};
+
+/* Device structure for ACPI hotplug slots. */
+struct acpihp_slot {
+	struct device			dev;
+	acpi_handle			handle;
+	u32				capabilities;
+	u32				flags;
+	enum acpihp_slot_type		type;
+	enum acpihp_slot_state		state;
+	struct acpihp_slot		*parent;
+	struct acpihp_slot_ops		*slot_ops;
+	void				*slot_data;
+	struct mutex			slot_mutex;
+	struct list_head		slot_list;
+	struct list_head		drvdata_list;
+	struct klist			dev_lists[ACPIHP_DEV_TYPE_MAX];
+	char				name[ACPIHP_SLOT_NAME_MAX_SIZE];
+};
+
+/* Device class for ACPI hotplug slots */
+extern struct class acpihp_slot_class;
+
+/* Register the ACPI hotplug slot class driver */
+extern int acpihp_register_class(void);
+
+/* Unregister the ACPI hotplug slot class driver */
+extern void acpihp_unregister_class(void);
+
+/* Utility routines */
+extern int acpihp_dev_get_type(acpi_handle handle, enum acpihp_dev_type *type);
+extern bool acpihp_dev_match_ids(struct acpi_device_info *infop, char **ids);
+extern char *acpihp_get_slot_type_name(enum acpihp_slot_type type);
+
+/* Mark/unmark an ACPI object as an ACPI hotplug slot. */
+extern acpi_status acpihp_mark_slot(acpi_handle handle,
+				    struct acpihp_slot *slot);
+extern acpi_status acpihp_unmark_slot(acpi_handle handle);
+
+/* Check whether the ACPI object is a hotplug slot. */
+extern bool acpihp_is_slot(acpi_handle handle);
+
+/* Interfaces to manage ACPI hotplug slots */
+extern struct acpihp_slot *acpihp_create_slot(acpi_handle handle, char *name);
+extern int acpihp_register_slot(struct acpihp_slot *slot);
+extern void acpihp_unregister_slot(struct acpihp_slot *slot);
+extern struct acpihp_slot *acpihp_slot_get(struct acpihp_slot *slot);
+extern void acpihp_slot_put(struct acpihp_slot *slot);
+extern bool acpihp_get_slot(acpi_handle handle, struct acpihp_slot **slot);
+
+/* Platform dependent hotplug hooks */
+extern acpi_status acpihp_slot_get_status(struct acpihp_slot *slot,
+					  u64 *status);
+extern acpi_status acpihp_slot_poweron(struct acpihp_slot *slot);
+extern acpi_status acpihp_slot_poweroff(struct acpihp_slot *slot);
+
+extern int acpihp_debug;
+
+#define ACPIHP_WARN(fmt, ...) \
+	pr_warn("acpihp@%s: " fmt,  __func__, ##__VA_ARGS__)
+
+#define ACPIHP_DEBUG(fmt, ...) \
+	do { \
+		if (acpihp_debug & 0x01) \
+			pr_warn("acpihp@%s: " fmt,  __func__, ##__VA_ARGS__); \
+	} while (0)
+
+#define	ACPIHP_SLOT_DEBUG(slot, fmt, ...) \
+	do { \
+		if (acpihp_debug & 0x01) \
+			dev_warn(&(slot)->dev, fmt, ##__VA_ARGS__); \
+	} while (0)
+
+#define	ACPIHP_SLOT_WARN(slot, fmt, ...) \
+	dev_warn(&(slot)->dev, fmt, ##__VA_ARGS__)
+
+#endif	/* CONFIG_ACPI_HOTPLUG */
+
+#endif	/* __ACPI_HOTPLUG_H__ */
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 02/28] ACPIHP: ACPI system device hotplug slot enumerator driver
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 01/28] ACPIHP: introduce a framework for ACPI based system device hotplug Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 03/28] ACPIHP: detect ACPI hotplug slots by checking ACPI _EJ0 method Jiang Liu
                   ` (26 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci, Gaohuai Han

An ACPI hotplug slot is an abstraction of receptacles, where a group of
system devices could be connected to. This patch implements the skeleton
of the ACPI system device hotplug slot enumerator. On loading, it scans
the whole ACPI namespace for hotplug slots and creates a device node for
each hotplug slot found. Every hotplug slot is associated with a device
class named acpihp_slot_class. Later hotplug drivers will register onto
acpihp_slot_class to manage all hotplug slots.

xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
Signed-off-by: Gaohuai Han <hangaohuai@huawei.com>
---
 drivers/acpi/Kconfig          |   14 ++
 drivers/acpi/hotplug/Makefile |    3 +
 drivers/acpi/hotplug/core.c   |    2 +-
 drivers/acpi/hotplug/slot.c   |  417 +++++++++++++++++++++++++++++++++++++++++
 4 files changed, 435 insertions(+), 1 deletion(-)
 create mode 100644 drivers/acpi/hotplug/slot.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 01b87c7..915a25e 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -334,6 +334,20 @@ menuconfig ACPI_HOTPLUG
 	  If your hardware and firmware do not support dynamic reconfiguration
 	  of system devices at runtime, you need not to enable this option.
 
+config ACPI_HOTPLUG_SLOT
+	tristate "Hotplug Slot Enumerator"
+	depends on ACPI_HOTPLUG
+	default y
+	help
+	  System devices could be dynamically configured at runtime through
+	  ACPI interfaces. This driver abstracts, creates and controls each
+	  system device dynamic configuration control point as an ACPI hotplug
+	  slot. And ACPI system device hotplug drivers will be used to manage
+	  those hotplug slots created by this driver.
+
+	  To compile this driver as a module, choose M here:
+	  the module will be called acpihp_slot.
+
 config ACPI_CONTAINER
 	tristate "Container and Module Devices (EXPERIMENTAL)"
 	depends on EXPERIMENTAL
diff --git a/drivers/acpi/hotplug/Makefile b/drivers/acpi/hotplug/Makefile
index 5e7790f..2cbb03c 100644
--- a/drivers/acpi/hotplug/Makefile
+++ b/drivers/acpi/hotplug/Makefile
@@ -4,3 +4,6 @@
 
 obj-$(CONFIG_ACPI_HOTPLUG)			+= acpihp.o
 acpihp-y					= core.o
+
+obj-$(CONFIG_ACPI_HOTPLUG_SLOT)			+= acpihp_slot.o
+acpihp_slot-y					= slot.o
diff --git a/drivers/acpi/hotplug/core.c b/drivers/acpi/hotplug/core.c
index 086f4e5..081b27f 100644
--- a/drivers/acpi/hotplug/core.c
+++ b/drivers/acpi/hotplug/core.c
@@ -360,7 +360,7 @@ acpi_status acpihp_slot_get_status(struct acpihp_slot *slot, u64 *status)
 		ACPIHP_SLOT_DEBUG(slot, "operation not supported.\n");
 		return AE_SUPPORT;
 	} else if (slot->slot_ops->get_status)
-		return slot->slot_ops->get_status(slot->handle, status);
+		return slot->slot_ops->get_status(slot, status);
 
 	rc = acpi_evaluate_integer(slot->handle, METHOD_NAME__UID,
 				   NULL, status);
diff --git a/drivers/acpi/hotplug/slot.c b/drivers/acpi/hotplug/slot.c
new file mode 100644
index 0000000..a230fc7
--- /dev/null
+++ b/drivers/acpi/hotplug/slot.c
@@ -0,0 +1,417 @@
+/*
+ * Copyright (C) 2012 Huawei Tech. Co., Ltd.
+ * Copyright (C) 2012 Jiang Liu <jiang.liu@huawei.com>
+ * Copyright (C) 2012 Gaohuai Han <hangaohuai@huawei.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include "acpihp.h"
+
+struct acpihp_slot_id {
+	struct list_head node;
+	enum acpihp_slot_type type;
+	unsigned long instance_id;
+};
+
+static LIST_HEAD(slot_list);
+static LIST_HEAD(slot_id_list);
+static struct acpihp_slot_ops *slot_ops_curr;
+
+/*
+ * Platform dependent slot drivers should be sorted in descending order.
+ * The first entry whose init() method returns success will be used.
+ */
+static struct acpihp_slot_ops *slot_ops_array[] = {
+	NULL
+};
+
+static acpi_status __init
+acpihp_slot_get_dev_type(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	u32 *tp = (u32 *)rv;
+	acpi_status status = AE_OK;
+	enum acpihp_dev_type type;
+
+	/* Only care about CPU, memory, PCI host bridge and CONTAINER */
+	if (!acpihp_dev_get_type(handle, &type)) {
+		switch (type) {
+		case ACPIHP_DEV_TYPE_CPU:
+			*tp |= 0x0001;
+			status = AE_CTRL_DEPTH;
+			break;
+		case ACPIHP_DEV_TYPE_MEM:
+			*tp |= 0x0002;
+			status = AE_CTRL_DEPTH;
+			break;
+		case ACPIHP_DEV_TYPE_HOST_BRIDGE:
+			*tp |= 0x0004;
+			status = AE_CTRL_DEPTH;
+			break;
+		case ACPIHP_DEV_TYPE_CONTAINER:
+			*tp |= 0x0008;
+			break;
+		default:
+			break;
+		}
+	}
+
+	return status;
+}
+
+static enum acpihp_slot_type __init acpihp_slot_get_type_child(u32 dev_types)
+{
+	BUG_ON(dev_types > 15);
+
+	switch (dev_types) {
+	case 0:
+		/* Generic CONTAINER */
+		return ACPIHP_SLOT_TYPE_COMMON;
+	case 1:
+		/* Physical processor with logical CPUs */
+		return ACPIHP_SLOT_TYPE_CPU;
+	case 2:
+		/* Memory board/box with memory devices */
+		return ACPIHP_SLOT_TYPE_MEM;
+	case 3:
+		/* Physical processor with CPUs and memory controllers */
+		return ACPIHP_SLOT_TYPE_CPU;
+	case 4:
+		/* IO eXtension board/box with IO host bridges */
+		return ACPIHP_SLOT_TYPE_IOX;
+	case 7:
+		/* Physical processor with CPUs, IO host bridges and MCs. */
+		return ACPIHP_SLOT_TYPE_CPU;
+	case 8:
+		/* Generic CONTAINER */
+		return ACPIHP_SLOT_TYPE_COMMON;
+	case 9:
+		/* System board with physical processors */
+		return ACPIHP_SLOT_TYPE_SYSTEM_BOARD;
+	case 11:
+		/* System board with physical processors and memory */
+		return ACPIHP_SLOT_TYPE_SYSTEM_BOARD;
+	case 15:
+		/* Node with processor, memory and IO host bridge */
+		return ACPIHP_SLOT_TYPE_NODE;
+	default:
+		return ACPIHP_SLOT_TYPE_UNKNOWN;
+	}
+}
+
+static enum acpihp_slot_type __init
+acpihp_slot_get_type_self(struct acpihp_slot *slot)
+{
+	enum acpihp_dev_type type;
+
+	if (acpihp_dev_get_type(slot->handle, &type))
+		return ACPIHP_SLOT_TYPE_UNKNOWN;
+
+	switch (type) {
+	case ACPIHP_DEV_TYPE_CPU:
+		/* Logical CPU used in virtualization environment */
+		return ACPIHP_SLOT_TYPE_CPU;
+	case ACPIHP_DEV_TYPE_MEM:
+		/* Memory board with single memory device */
+		return ACPIHP_SLOT_TYPE_MEM;
+	case ACPIHP_DEV_TYPE_HOST_BRIDGE:
+		/* IO eXtension board/box with single IO host bridge */
+		return ACPIHP_SLOT_TYPE_IOX;
+	default:
+		return ACPIHP_SLOT_TYPE_UNKNOWN;
+	}
+}
+
+/*
+ * Generate a meaningful name for a hotplug slot according to devices
+ * connecting to it.
+ */
+static int __init acpihp_slot_generate_name(struct acpihp_slot *slot)
+{
+	int found = 0;
+	u32 child_types = 0;
+	unsigned long long uid;
+	struct acpihp_slot_id *slot_id;
+
+	/*
+	 * Figure out slot type by checking ACPI device type of the slot.
+	 * Check child ACPI device types if the slot is a CONTAINER.
+	 */
+	slot->type = acpihp_slot_get_type_self(slot);
+	if (slot->type == ACPIHP_SLOT_TYPE_UNKNOWN) {
+		acpi_walk_namespace(ACPI_TYPE_DEVICE, slot->handle,
+				ACPI_UINT32_MAX, acpihp_slot_get_dev_type,
+				NULL, NULL, (void **)&child_types);
+		acpi_walk_namespace(ACPI_TYPE_PROCESSOR, slot->handle,
+				ACPI_UINT32_MAX, acpihp_slot_get_dev_type,
+				NULL, NULL, (void **)&child_types);
+		slot->type = acpihp_slot_get_type_child(child_types);
+	}
+
+	/* Respect firmware settings if ACPI _UID returns a valid value. */
+	if (ACPI_SUCCESS(acpi_evaluate_integer(slot->handle, METHOD_NAME__UID,
+					       NULL, &uid)))
+		goto set_name;
+
+	list_for_each_entry(slot_id, &slot_id_list, node)
+		if (slot_id->type == slot->type) {
+			found = 1;
+			break;
+		}
+	if (!found) {
+		slot_id = kzalloc(sizeof(struct acpihp_slot_id), GFP_KERNEL);
+		if (!slot_id) {
+			ACPIHP_SLOT_WARN(slot,
+				"fails to allocate slot instance ID.\n");
+			return -ENOMEM;
+		}
+		slot_id->type = slot->type;
+		list_add_tail(&slot_id->node, &slot_id_list);
+	}
+
+	uid = slot_id->instance_id++;
+
+set_name:
+	snprintf(slot->name, sizeof(slot->name) - 1, "%s%02llx",
+		 acpihp_get_slot_type_name(slot->type), uid);
+	dev_set_name(&slot->dev, "%s", slot->name);
+
+	return 0;
+}
+
+static int __init acpihp_slot_get_parent(struct acpihp_slot *slot)
+{
+	acpi_handle handle, root_handle;
+	struct acpihp_slot *tmp;
+
+	slot->parent = NULL;
+	handle = slot->handle;
+	if (ACPI_FAILURE(acpi_get_handle(NULL, ACPI_NS_ROOT_PATH,
+					 &root_handle))) {
+		ACPIHP_SLOT_DEBUG(slot, "fails to get ACPI root device.\n");
+		return -ENODEV;
+	}
+
+	do {
+		if (ACPI_FAILURE(acpi_get_parent(handle, &handle))) {
+			ACPIHP_SLOT_DEBUG(slot,
+					  "fails to get parent device.\n");
+			return -ENODEV;
+		}
+		list_for_each_entry(tmp, &slot_list, slot_list)
+			if (tmp->handle == handle) {
+				slot->parent = tmp;
+				return 0;
+			}
+	} while (handle != root_handle);
+
+	return 0;
+}
+
+static int __init acpihp_slot_get_state(struct acpihp_slot *slot)
+{
+	unsigned long long sta;
+
+	/* Hotplug slots must implement ACPI _STA method. */
+	if (ACPI_FAILURE(acpihp_slot_get_status(slot, &sta))) {
+		ACPIHP_SLOT_DEBUG(slot, "fails to execute _STA method.\n");
+		return -EINVAL;
+	}
+
+	if (!(sta & ACPI_STA_DEVICE_PRESENT))
+		slot->state = ACPIHP_SLOT_STATE_ABSENT;
+	else if ((sta & ACPI_STA_DEVICE_ENABLED) ||
+		 (sta & ACPI_STA_DEVICE_FUNCTIONING))
+		slot->state = ACPIHP_SLOT_STATE_POWERED;
+	else
+		slot->state = ACPIHP_SLOT_STATE_PRESENT;
+
+	return 0;
+}
+
+static int __init acpihp_slot_create(acpi_handle handle)
+{
+	struct acpihp_slot *slot;
+
+	slot = acpihp_create_slot(handle, NULL);
+	if (!slot) {
+		ACPIHP_DEBUG("fails to allocate memory for slot %p.\n", handle);
+		return -ENOMEM;
+	}
+
+	slot->slot_ops = slot_ops_curr;
+	if (ACPI_FAILURE(slot_ops_curr->create(slot))) {
+		ACPIHP_DEBUG("fails to create slot for %p.\n", handle);
+		goto out_free;
+	}
+	if (acpihp_slot_get_parent(slot))
+		goto out_destroy;
+	if (acpihp_slot_get_state(slot))
+		goto out_destroy;
+	if (acpihp_slot_generate_name(slot))
+		goto out_destroy;
+
+	if (acpihp_register_slot(slot)) {
+		ACPIHP_SLOT_DEBUG(slot, "fails to register slot.\n");
+		goto out_destroy;
+	}
+	if (ACPI_FAILURE(acpihp_mark_slot(handle, slot))) {
+		ACPIHP_SLOT_DEBUG(slot,
+			"fails to attach slot to ACPI device object.\n");
+		goto out_unregister;
+	}
+
+	list_add_tail(&slot->slot_list, &slot_list);
+
+	return 0;
+
+out_unregister:
+	acpihp_unregister_slot(slot);
+out_destroy:
+	if (slot_ops_curr->destroy)
+		slot_ops_curr->destroy(slot);
+out_free:
+	acpihp_slot_put(slot);
+	return -ENODEV;
+}
+
+static acpi_status __init acpihp_slot_scan(acpi_handle handle, u32 lvl,
+					   void *context, void **rv)
+{
+	enum acpihp_dev_type type;
+
+	if (acpihp_dev_get_type(handle, &type) ||
+	    type == ACPIHP_DEV_TYPE_UNKNOWN)
+		return AE_OK;
+
+	if (ACPI_SUCCESS(slot_ops_curr->check(handle)))
+		acpihp_slot_create(handle);
+
+	/*
+	 * Don't scan hotplug slots under PCI host bridges, they should be
+	 * handled by acpiphp or pciehp drivers.
+	 */
+	if (type == ACPIHP_DEV_TYPE_HOST_BRIDGE)
+		return AE_CTRL_DEPTH;
+
+	return AE_OK;
+}
+
+static int __init acpihp_slot_scan_slots(void)
+{
+	acpi_status status;
+
+	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+				     ACPI_UINT32_MAX, acpihp_slot_scan,
+				     NULL, NULL, NULL);
+	if (!ACPI_SUCCESS(status))
+		goto out_err;
+
+	status = acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
+				     ACPI_UINT32_MAX, acpihp_slot_scan,
+				     NULL, NULL, NULL);
+	if (ACPI_SUCCESS(status))
+		return 0;
+
+out_err:
+	ACPIHP_DEBUG("fails to scan ACPI hotplug slots.\n");
+
+	return -ENODEV;
+}
+
+static void acpihp_slot_cleanup(void)
+{
+	struct acpihp_slot *slot, *tmp;
+	struct acpihp_slot_id *slot_id, *tmp_id;
+
+	list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
+		acpihp_unmark_slot(slot->handle);
+		acpihp_unregister_slot(slot);
+		if (slot->slot_ops && slot->slot_ops->destroy)
+			slot->slot_ops->destroy(slot);
+		acpihp_slot_put(slot);
+	}
+
+	list_for_each_entry_safe(slot_id, tmp_id, &slot_id_list, node) {
+		list_del(&slot_id->node);
+		kfree(slot_id);
+	}
+}
+
+static int __init acpihp_slot_init(void)
+{
+	int i, retval;
+
+	/* probe for available platform specific slot driver. */
+	for (i = 0; slot_ops_array[i]; i++)
+		if (ACPI_SUCCESS(slot_ops_array[i]->init())) {
+			slot_ops_curr = slot_ops_array[i];
+			break;
+		}
+	if (slot_ops_curr == NULL) {
+		ACPIHP_DEBUG("no platform specific slot driver available.\n");
+		return -ENODEV;
+	}
+
+	retval = acpihp_register_class();
+	if (retval != 0) {
+		ACPIHP_DEBUG("fails to register ACPI hotplug slot class.\n");
+		goto out_fini;
+	}
+
+	if (acpihp_slot_scan_slots() != 0)
+		ACPIHP_DEBUG("fails to enumerate ACPI hotplug slots.\n");
+
+	/* Back out if no ACPI hotplug slot found. */
+	if (list_empty(&slot_list)) {
+		ACPIHP_DEBUG("no ACPI hotplug slot available.\n");
+		retval = -ENODEV;
+		goto out_unregister_class;
+	}
+
+	return 0;
+
+out_unregister_class:
+	acpihp_unregister_class();
+out_fini:
+	if (slot_ops_curr && slot_ops_curr->fini)
+		slot_ops_curr->fini();
+	ACPIHP_DEBUG("fails to initialize ACPI hotplug slot driver.\n");
+
+	return retval;
+}
+
+static void __exit acpihp_slot_exit(void)
+{
+	acpihp_slot_cleanup();
+	acpihp_unregister_class();
+	if (slot_ops_curr && slot_ops_curr->fini)
+		slot_ops_curr->fini();
+}
+
+module_init(acpihp_slot_init);
+module_exit(acpihp_slot_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jiang Liu <jiang.liu@huawei.com>");
+MODULE_AUTHOR("Gaohuai Han <hangaohuai@huawei.com>");
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 03/28] ACPIHP: detect ACPI hotplug slots by checking ACPI _EJ0 method
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 01/28] ACPIHP: introduce a framework for ACPI based system device hotplug Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 02/28] ACPIHP: ACPI system device hotplug slot enumerator driver Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 04/28] ACPIHP: implement a fake ACPI system device hotplug slot enumerator Jiang Liu
                   ` (25 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci, Gaohuai Han

This patch implements a default mechanism to detect and manage ACPI
system device hotplug slots based on standard ACPI interfaces.
1) Detech hotplug slot by checking existence of _EJ0 and _STA methods.
2) Power off a slot by evaluating _EJ0 method.

It's the default hotplug slot enumerating mechanism, platform specifc
drivers may provide advanced implementation to override the default
implementation.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
Signed-off-by: Gaohuai Han <hangaohuai@huawei.com>
---
 drivers/acpi/hotplug/Makefile   |    1 +
 drivers/acpi/hotplug/acpihp.h   |    1 +
 drivers/acpi/hotplug/slot.c     |    1 +
 drivers/acpi/hotplug/slot_ej0.c |  153 +++++++++++++++++++++++++++++++++++++++
 4 files changed, 156 insertions(+)
 create mode 100644 drivers/acpi/hotplug/slot_ej0.c

diff --git a/drivers/acpi/hotplug/Makefile b/drivers/acpi/hotplug/Makefile
index 2cbb03c..5420ae7 100644
--- a/drivers/acpi/hotplug/Makefile
+++ b/drivers/acpi/hotplug/Makefile
@@ -7,3 +7,4 @@ acpihp-y					= core.o
 
 obj-$(CONFIG_ACPI_HOTPLUG_SLOT)			+= acpihp_slot.o
 acpihp_slot-y					= slot.o
+acpihp_slot-y					+= slot_ej0.o
diff --git a/drivers/acpi/hotplug/acpihp.h b/drivers/acpi/hotplug/acpihp.h
index 7467895..278c8c2 100644
--- a/drivers/acpi/hotplug/acpihp.h
+++ b/drivers/acpi/hotplug/acpihp.h
@@ -28,5 +28,6 @@
 #include <acpi/acpi_hotplug.h>
 
 extern struct acpi_device *acpi_root;
+extern struct acpihp_slot_ops acpihp_slot_ej0;
 
 #endif
diff --git a/drivers/acpi/hotplug/slot.c b/drivers/acpi/hotplug/slot.c
index a230fc7..8fdae61 100644
--- a/drivers/acpi/hotplug/slot.c
+++ b/drivers/acpi/hotplug/slot.c
@@ -43,6 +43,7 @@ static struct acpihp_slot_ops *slot_ops_curr;
  * The first entry whose init() method returns success will be used.
  */
 static struct acpihp_slot_ops *slot_ops_array[] = {
+	&acpihp_slot_ej0,
 	NULL
 };
 
diff --git a/drivers/acpi/hotplug/slot_ej0.c b/drivers/acpi/hotplug/slot_ej0.c
new file mode 100644
index 0000000..caaa25e
--- /dev/null
+++ b/drivers/acpi/hotplug/slot_ej0.c
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2012 Huawei Tech. Co., Ltd.
+ * Copyright (C) 2012 Gaohuai Han <hangaohuai@huawei.com>
+ * Copyright (C) 2012 Jiang Liu <jiang.liu@huawei.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/acpi.h>
+#include <acpi/acpi.h>
+#include <acpi/acpi_hotplug.h>
+
+static acpi_status acpihp_slot_ej0_capable(acpi_handle handle)
+{
+	acpi_handle tmp;
+
+	/* Assume a device object with _EJ0 and _STA has a hotplug slot. */
+	if (ACPI_FAILURE(acpi_get_handle(handle, "_EJ0", &tmp)))
+		return AE_ERROR;
+	if (ACPI_FAILURE(acpi_get_handle(handle, METHOD_NAME__STA, &tmp)))
+		return AE_ERROR;
+
+	return AE_OK;
+}
+
+static acpi_status
+acpihp_slot_ej0_check(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	acpi_status rc = AE_OK;
+	enum acpihp_dev_type type;
+
+	if (!acpihp_dev_get_type(handle, &type)) {
+		switch (type) {
+		case ACPIHP_DEV_TYPE_CPU:
+		case ACPIHP_DEV_TYPE_MEM:
+		case ACPIHP_DEV_TYPE_HOST_BRIDGE:
+		case ACPIHP_DEV_TYPE_CONTAINER:
+			if (ACPI_SUCCESS(acpihp_slot_ej0_capable(handle))) {
+				*(int *)rv = 1;
+				rc = AE_CTRL_TERMINATE;
+			} else if (type == ACPIHP_DEV_TYPE_HOST_BRIDGE) {
+				rc = AE_CTRL_DEPTH;
+			}
+			break;
+		default:
+			break;
+		}
+	}
+
+	return rc;
+}
+
+static acpi_status acpihp_slot_ej0_init(void)
+{
+	int cap = 0;
+
+	acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
+			ACPI_UINT32_MAX, acpihp_slot_ej0_check,
+			NULL, NULL, (void **)&cap);
+	acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
+			ACPI_UINT32_MAX, acpihp_slot_ej0_check,
+			NULL, NULL, (void **)&cap);
+
+	return cap ? AE_OK : AE_ERROR;
+}
+
+static void acpihp_slot_ej0_fini(void)
+{
+}
+
+static acpi_status acpihp_slot_ej0_create(struct acpihp_slot *slot)
+{
+	/*
+	 * Assume device objects with _EJ0 are capable of:
+	 * ONLINE, OFFLINE, POWEROFF, HOTPLUG
+	 */
+	slot->capabilities = ACPIHP_SLOT_CAP_ONLINE | ACPIHP_SLOT_CAP_OFFLINE |
+			     ACPIHP_SLOT_CAP_POWEROFF | ACPIHP_SLOT_CAP_HOTPLUG;
+
+	return AE_OK;
+}
+
+static acpi_status
+acpihp_slot_ej0_get_status(struct acpihp_slot *slot, u64 *status)
+{
+	acpi_status rc;
+
+	rc = acpi_evaluate_integer(slot->handle, METHOD_NAME__STA,
+				   NULL, status);
+	if (ACPI_FAILURE(rc))
+		ACPIHP_SLOT_DEBUG(slot, "fails to evaluate _STA method\n");
+
+	return rc;
+}
+
+static acpi_status acpihp_slot_ej0_poweroff(struct acpihp_slot *slot)
+{
+	acpi_status status;
+	unsigned long long sta;
+	union acpi_object arg;
+	struct acpi_object_list arg_list;
+	acpi_handle handle = slot->handle;
+
+	arg_list.count = 1;
+	arg_list.pointer = &arg;
+	arg.type = ACPI_TYPE_INTEGER;
+	arg.integer.value = 1;
+	status = acpi_evaluate_object(handle, "_EJ0", &arg_list, NULL);
+	if (ACPI_FAILURE(status)) {
+		ACPIHP_SLOT_WARN(slot, "fails to power off.\n");
+		return status;
+	}
+
+	status = acpi_evaluate_integer(handle, METHOD_NAME__STA, NULL, &sta);
+	if (ACPI_FAILURE(status)) {
+		ACPIHP_SLOT_WARN(slot, "fails to evaluate _STA method\n");
+		return status;
+	}
+
+	if (sta & (ACPI_STA_DEVICE_FUNCTIONING | ACPI_STA_DEVICE_ENABLED)) {
+		ACPIHP_SLOT_WARN(slot,
+			 "object is still active after executing _EJ0.\n");
+		return AE_ERROR;
+	}
+
+	return AE_OK;
+}
+
+struct acpihp_slot_ops acpihp_slot_ej0 = {
+	.owner = THIS_MODULE,
+	.desc = "Detect Hotplug Slots by Checking ACPI _EJ0 Method",
+	.init = acpihp_slot_ej0_init,
+	.fini = acpihp_slot_ej0_fini,
+	.check = acpihp_slot_ej0_capable,
+	.create = acpihp_slot_ej0_create,
+	.poweroff = acpihp_slot_ej0_poweroff,
+	.get_status = acpihp_slot_ej0_get_status,
+};
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 04/28] ACPIHP: implement a fake ACPI system device hotplug slot enumerator
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (2 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 03/28] ACPIHP: detect ACPI hotplug slots by checking ACPI _EJ0 method Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 05/28] ACPI: introduce interfaces to manage ACPI device reference count Jiang Liu
                   ` (24 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

This patch implements a fake ACPI system device hotplug slot enumerator,
which could be used to test and verify hotplug logic on platforms with
hardware hotplug capabilities.

The fake slot enumerator will be enabled by passing module parameter
"fake_slot=value". The encoding of "value" is:
0x1: fake ACPI CONTAINER device as hotplug slot
0x2: fake ACPI memory device as hotplug slot
0x4: fake ACPI Processor object or Processor device as hotplug slot
0x8: fake ACPI PCI host bridge device as hotplug slot.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
---
 drivers/acpi/Kconfig             |    9 ++
 drivers/acpi/hotplug/Makefile    |    1 +
 drivers/acpi/hotplug/acpihp.h    |    3 +
 drivers/acpi/hotplug/slot.c      |    3 +
 drivers/acpi/hotplug/slot_fake.c |  177 ++++++++++++++++++++++++++++++++++++++
 5 files changed, 193 insertions(+)
 create mode 100644 drivers/acpi/hotplug/slot_fake.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 915a25e..5a5a3e5 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -348,6 +348,15 @@ config ACPI_HOTPLUG_SLOT
 	  To compile this driver as a module, choose M here:
 	  the module will be called acpihp_slot.
 
+config ACPI_HOTPLUG_SLOT_FAKE
+	bool "Fake Hotplug Slot Enumerator"
+	depends on ACPI_HOTPLUG_SLOT
+	default y
+	help
+	  Enable the fake ACPI system device hotplug slot enumerator, which
+	  could be used to test hotplug functionalities on hardware platforms
+	  with out system device hotplug capabilities.
+
 config ACPI_CONTAINER
 	tristate "Container and Module Devices (EXPERIMENTAL)"
 	depends on EXPERIMENTAL
diff --git a/drivers/acpi/hotplug/Makefile b/drivers/acpi/hotplug/Makefile
index 5420ae7..c19b350 100644
--- a/drivers/acpi/hotplug/Makefile
+++ b/drivers/acpi/hotplug/Makefile
@@ -8,3 +8,4 @@ acpihp-y					= core.o
 obj-$(CONFIG_ACPI_HOTPLUG_SLOT)			+= acpihp_slot.o
 acpihp_slot-y					= slot.o
 acpihp_slot-y					+= slot_ej0.o
+acpihp_slot-$(CONFIG_ACPI_HOTPLUG_SLOT_FAKE)	+= slot_fake.o
diff --git a/drivers/acpi/hotplug/acpihp.h b/drivers/acpi/hotplug/acpihp.h
index 278c8c2..7c49eab 100644
--- a/drivers/acpi/hotplug/acpihp.h
+++ b/drivers/acpi/hotplug/acpihp.h
@@ -29,5 +29,8 @@
 
 extern struct acpi_device *acpi_root;
 extern struct acpihp_slot_ops acpihp_slot_ej0;
+#ifdef	CONFIG_ACPI_HOTPLUG_SLOT_FAKE
+extern struct acpihp_slot_ops acpihp_slot_fake;
+#endif
 
 #endif
diff --git a/drivers/acpi/hotplug/slot.c b/drivers/acpi/hotplug/slot.c
index 8fdae61..38f39dc 100644
--- a/drivers/acpi/hotplug/slot.c
+++ b/drivers/acpi/hotplug/slot.c
@@ -44,6 +44,9 @@ static struct acpihp_slot_ops *slot_ops_curr;
  */
 static struct acpihp_slot_ops *slot_ops_array[] = {
 	&acpihp_slot_ej0,
+#ifdef	CONFIG_ACPI_HOTPLUG_SLOT_FAKE
+	&acpihp_slot_fake,
+#endif
 	NULL
 };
 
diff --git a/drivers/acpi/hotplug/slot_fake.c b/drivers/acpi/hotplug/slot_fake.c
new file mode 100644
index 0000000..512e09b
--- /dev/null
+++ b/drivers/acpi/hotplug/slot_fake.c
@@ -0,0 +1,177 @@
+/*
+ * Copyright (C) 2012 Huawei Tech. Co., Ltd.
+ * Copyright (C) 2012 Jiang Liu <jiang.liu@huawei.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/acpi.h>
+#include <acpi/acpi.h>
+#include <acpi/acpi_hotplug.h>
+#include "acpihp.h"
+
+struct acpihp_slot_fake_data {
+	bool enabled;
+};
+
+/*
+ * Encoding of the fake_slot parameter:
+ * 0x1: fake ACPI CONTAINER device as hotplug slot
+ * 0x2: fake ACPI memory device as hotplug slot
+ * 0x4: fake ACPI Processor object or Processor device as hotplug slot
+ * 0x8: fake ACPI PCI host bridge device as hotplug slot.
+ * The above encoding must be kept in consistence with definition of
+ * 'enum acpihp_dev_type'.
+ */
+int acpihp_fake_slot;
+module_param_named(fake_slot, acpihp_fake_slot, int, S_IRUGO);
+MODULE_PARM_DESC(fake_slot, "fake ACPI hotplug slots, 0x1 for CONTAINER, 0x2 for memory, 0x4 for CPU, 0x8 for PCI host bridge.");
+
+static acpi_status acpihp_slot_fake_init(void)
+{
+	return acpihp_fake_slot ? AE_OK : AE_ERROR;
+}
+
+static acpi_status
+acpihp_slot_fake_check(acpi_handle handle, u32 lvl, void *context, void **rv)
+{
+	int *valid = (int *)rv;
+	enum acpihp_dev_type type;
+
+	if (!acpihp_dev_get_type(handle, &type)) {
+		switch (type) {
+		case ACPIHP_DEV_TYPE_CPU:
+		case ACPIHP_DEV_TYPE_MEM:
+		case ACPIHP_DEV_TYPE_HOST_BRIDGE:
+			*valid = 1;
+			return AE_CTRL_TERMINATE;
+		default:
+			break;
+		}
+	}
+
+	return AE_OK;
+}
+
+static acpi_status acpihp_slot_fake_capable(acpi_handle handle)
+{
+	int valid = 0;
+	acpi_status rc;
+	unsigned long long sta;
+	enum acpihp_dev_type type;
+
+	/* Only care about CPU, memory, PCI host bridge and CONTAINER */
+	if (acpihp_dev_get_type(handle, &type))
+		return AE_ERROR;
+	if (type == ACPIHP_DEV_TYPE_CPU || type == ACPIHP_DEV_TYPE_MEM ||
+	    type == ACPIHP_DEV_TYPE_HOST_BRIDGE) {
+		if (acpihp_fake_slot & (1 << (type - 1)))
+			valid = 1;
+	} else if (type == ACPIHP_DEV_TYPE_CONTAINER &&
+		   acpihp_fake_slot & (1 << (type - 1))) {
+		acpi_walk_namespace(ACPI_TYPE_DEVICE, handle,
+				ACPI_UINT32_MAX, acpihp_slot_fake_check,
+				NULL, NULL, (void **)&valid);
+		acpi_walk_namespace(ACPI_TYPE_PROCESSOR, handle,
+				ACPI_UINT32_MAX, acpihp_slot_fake_check,
+				NULL, NULL, (void **)&valid);
+	}
+	if (valid == 0)
+		return AE_ERROR;
+
+	/* Check whether device is present and enabled. */
+	rc = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+	if (rc == AE_NOT_FOUND)
+		sta = ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED;
+	else if (ACPI_FAILURE(rc))
+		sta = 0;
+	else if (sta & ACPI_STA_DEVICE_FUNCTIONING)
+		sta |= ACPI_STA_DEVICE_ENABLED;
+	if (!(sta & ACPI_STA_DEVICE_PRESENT) ||
+	    !(sta & ACPI_STA_DEVICE_ENABLED))
+		return AE_ERROR;
+
+	return AE_OK;
+}
+
+static acpi_status acpihp_slot_fake_create(struct acpihp_slot *slot)
+{
+	struct acpihp_slot_fake_data *data;
+
+	data = kzalloc(sizeof(*data), GFP_KERNEL);
+	if (!data)
+		return AE_ERROR;
+
+	data->enabled = true;
+	slot->slot_data = data;
+	slot->capabilities = ACPIHP_SLOT_CAP_ONLINE | ACPIHP_SLOT_CAP_OFFLINE;
+
+	return AE_OK;
+}
+
+static void acpihp_slot_fake_destroy(struct acpihp_slot *slot)
+{
+	struct acpihp_slot_fake_data *data = slot->slot_data;
+
+	slot->slot_data = NULL;
+	kfree(data);
+}
+
+static acpi_status
+acpihp_slot_fake_get_status(struct acpihp_slot *slot, u64 *status)
+{
+	struct acpihp_slot_fake_data *data = slot->slot_data;
+
+	if (data->enabled)
+		*status = ACPI_STA_DEVICE_PRESENT | ACPI_STA_DEVICE_ENABLED |
+			  ACPI_STA_DEVICE_FUNCTIONING;
+	else
+		*status = ACPI_STA_DEVICE_PRESENT;
+
+	return AE_OK;
+}
+
+static acpi_status acpihp_slot_fake_poweron(struct acpihp_slot *slot)
+{
+	struct acpihp_slot_fake_data *data = slot->slot_data;
+
+	data->enabled = true;
+
+	return AE_OK;
+}
+
+static acpi_status acpihp_slot_fake_poweroff(struct acpihp_slot *slot)
+{
+	struct acpihp_slot_fake_data *data = slot->slot_data;
+
+	data->enabled = false;
+
+	return AE_OK;
+}
+
+struct acpihp_slot_ops acpihp_slot_fake = {
+	.owner = THIS_MODULE,
+	.desc = "Fake ACPI System Device Hotplug Slots for Testing",
+	.init = acpihp_slot_fake_init,
+	.check = acpihp_slot_fake_capable,
+	.create = acpihp_slot_fake_create,
+	.destroy = acpihp_slot_fake_destroy,
+	.poweron = acpihp_slot_fake_poweron,
+	.poweroff = acpihp_slot_fake_poweroff,
+	.get_status = acpihp_slot_fake_get_status,
+};
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 05/28] ACPI: introduce interfaces to manage ACPI device reference count
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (3 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 04/28] ACPIHP: implement a fake ACPI system device hotplug slot enumerator Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 06/28] ACPIHP: introduce interfaces to scan and walk ACPI devices connecting to a slot Jiang Liu
                   ` (23 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

Function acpi_bus_get_device() return an ACPI device without holding a
reference count the device, which is not safe to ACPI hotplug operations.

So change acpi_bus_get_device() to hold a reference count on the returned
ACPI device and introduces acpi_get_device()/acpi_put_device() to manage
ACPI device reference count.

This patch is still incomplete, need to check and modify all callers of
acpi_bus_get_device().

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
Signed-off-by: Hanjun Guo <guohanjun@huawei.com>
---
 drivers/acpi/bus.c                     |   22 +++++++++++++++++++---
 drivers/acpi/internal.h                |    3 +++
 drivers/acpi/scan.c                    |    6 ++++++
 drivers/gpu/drm/i915/intel_opregion.c  |    2 ++
 drivers/gpu/drm/nouveau/nouveau_acpi.c |    1 +
 drivers/pci/hotplug/acpiphp_glue.c     |    9 +++++++--
 drivers/pci/hotplug/acpiphp_ibm.c      |    5 ++++-
 drivers/pci/hotplug/sgi_hotplug.c      |    6 +++++-
 drivers/platform/x86/thinkpad_acpi.c   |    2 ++
 drivers/pnp/pnpacpi/core.c             |   23 ++++++++++-------------
 include/acpi/acpi_bus.h                |   16 +++++++++++++++-
 11 files changed, 74 insertions(+), 21 deletions(-)

diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c
index e059695..1f85b0b 100644
--- a/drivers/acpi/bus.c
+++ b/drivers/acpi/bus.c
@@ -89,26 +89,42 @@ static struct dmi_system_id dsdt_dmi_table[] __initdata = {
                                 Device Management
    -------------------------------------------------------------------------- */
 
-int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device)
+bool acpi_bus_has_device(acpi_handle handle)
 {
-	acpi_status status = AE_OK;
+	acpi_status status;
+	struct acpi_device *device = NULL;
+
+	down_read(&acpi_device_data_handler_sem);
+	status = acpi_get_data(handle, acpi_bus_data_handler, (void **)&device);
+	up_read(&acpi_device_data_handler_sem);
+
+	return ACPI_SUCCESS(status) && device;
+}
+EXPORT_SYMBOL(acpi_bus_has_device);
 
+int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device)
+{
+	acpi_status status;
 
 	if (!device)
 		return -EINVAL;
 
 	/* TBD: Support fixed-feature devices */
 
+	down_read(&acpi_device_data_handler_sem);
 	status = acpi_get_data(handle, acpi_bus_data_handler, (void **)device);
 	if (ACPI_FAILURE(status) || !*device) {
+		up_read(&acpi_device_data_handler_sem);
 		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No context for object [%p]\n",
 				  handle));
 		return -ENODEV;
+	} else {
+		get_device(&(*device)->dev);
+		up_read(&acpi_device_data_handler_sem);
 	}
 
 	return 0;
 }
-
 EXPORT_SYMBOL(acpi_bus_get_device);
 
 acpi_status acpi_bus_get_status_handle(acpi_handle handle,
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index ca75b9c..70843c0 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -93,4 +93,7 @@ static inline int suspend_nvs_save(void) { return 0; }
 static inline void suspend_nvs_restore(void) {}
 #endif
 
+extern struct rw_semaphore acpi_device_data_handler_sem;
+void acpi_bus_data_handler(acpi_handle handle, void *context);
+
 #endif /* _ACPI_INTERNAL_H_ */
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index d1ecca2..a1d5188 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -10,6 +10,7 @@
 #include <linux/signal.h>
 #include <linux/kthread.h>
 #include <linux/dmi.h>
+#include <linux/sem.h>
 
 #include <acpi/acpi_drivers.h>
 
@@ -32,6 +33,7 @@ static LIST_HEAD(acpi_device_list);
 static LIST_HEAD(acpi_bus_id_list);
 DEFINE_MUTEX(acpi_device_lock);
 LIST_HEAD(acpi_wakeup_device_list);
+DECLARE_RWSEM(acpi_device_data_handler_sem);
 
 struct acpi_device_bus_id{
 	char bus_id[15];
@@ -552,7 +554,9 @@ static void acpi_device_unregister(struct acpi_device *device, int type)
 	list_del(&device->wakeup_list);
 	mutex_unlock(&acpi_device_lock);
 
+	down_write(&acpi_device_data_handler_sem);
 	acpi_detach_data(device->handle, acpi_bus_data_handler);
+	up_write(&acpi_device_data_handler_sem);
 
 	acpi_device_remove_files(device);
 	device_unregister(&device->dev);
@@ -1215,8 +1219,10 @@ static int acpi_device_set_context(struct acpi_device *device)
 	if (!device->handle)
 		return 0;
 
+	down_write(&acpi_device_data_handler_sem);
 	status = acpi_attach_data(device->handle,
 				  acpi_bus_data_handler, device);
+	up_write(&acpi_device_data_handler_sem);
 	if (ACPI_SUCCESS(status))
 		return 0;
 
diff --git a/drivers/gpu/drm/i915/intel_opregion.c b/drivers/gpu/drm/i915/intel_opregion.c
index 18bd0af..da7dd48 100644
--- a/drivers/gpu/drm/i915/intel_opregion.c
+++ b/drivers/gpu/drm/i915/intel_opregion.c
@@ -359,6 +359,8 @@ static void intel_didl_outputs(struct drm_device *dev)
 		}
 	}
 
+	acpi_device_put(acpi_dev);
+
 	if (!acpi_video_bus) {
 		pr_warn("No ACPI video bus found\n");
 		return;
diff --git a/drivers/gpu/drm/nouveau/nouveau_acpi.c b/drivers/gpu/drm/nouveau/nouveau_acpi.c
index 26ebffe..69d2e22 100644
--- a/drivers/gpu/drm/nouveau/nouveau_acpi.c
+++ b/drivers/gpu/drm/nouveau/nouveau_acpi.c
@@ -417,6 +417,7 @@ nouveau_acpi_edid(struct drm_device *dev, struct drm_connector *connector)
 		return -ENODEV;
 
 	ret = acpi_video_get_edid(acpidev, type, -1, &edid);
+	acpi_device_put(acpidev);
 	if (ret < 0)
 		return ret;
 
diff --git a/drivers/pci/hotplug/acpiphp_glue.c b/drivers/pci/hotplug/acpiphp_glue.c
index 3d6d4fd..1016c38 100644
--- a/drivers/pci/hotplug/acpiphp_glue.c
+++ b/drivers/pci/hotplug/acpiphp_glue.c
@@ -749,11 +749,13 @@ static int acpiphp_bus_add(struct acpiphp_func *func)
 		 * the bus then re-add it...
 		 */
 		ret_val = acpi_bus_trim(device, 1);
+		acpi_device_put(device);
 		dbg("acpi_bus_trim return %x\n", ret_val);
 	}
 
 	ret_val = acpi_bus_add(&device, pdevice, func->handle,
 		ACPI_BUS_TYPE_DEVICE);
+	acpi_device_put(pdevice);
 	if (ret_val) {
 		dbg("error adding bus, %x\n",
 			-ret_val);
@@ -785,6 +787,8 @@ static int acpiphp_bus_trim(acpi_handle handle)
 	if (retval)
 		err("cannot remove from acpi list\n");
 
+	acpi_device_put(device);
+
 	return retval;
 }
 
@@ -1146,8 +1150,10 @@ static void handle_bridge_insertion(acpi_handle handle, u32 type)
 	}
 	if (acpi_bus_add(&device, pdevice, handle, ACPI_BUS_TYPE_DEVICE)) {
 		err("cannot add bridge to acpi list\n");
+		acpi_device_put(pdevice);
 		return;
 	}
+	acpi_device_put(pdevice);
 	if (!acpiphp_configure_bridge(handle) &&
 		!acpi_bus_start(device))
 		add_bridge(handle);
@@ -1224,7 +1230,6 @@ static void _handle_hotplug_event_bridge(struct work_struct *work)
 	char objname[64];
 	struct acpi_buffer buffer = { .length = sizeof(objname),
 				      .pointer = objname };
-	struct acpi_device *device;
 	int num_sub_bridges = 0;
 	struct acpiphp_hp_work *hp_work;
 	acpi_handle handle;
@@ -1234,7 +1239,7 @@ static void _handle_hotplug_event_bridge(struct work_struct *work)
 	handle = hp_work->handle;
 	type = hp_work->type;
 
-	if (acpi_bus_get_device(handle, &device)) {
+	if (!acpi_bus_has_device(handle)) {
 		/* This bridge must have just been physically inserted */
 		handle_bridge_insertion(handle, type);
 		goto out;
diff --git a/drivers/pci/hotplug/acpiphp_ibm.c b/drivers/pci/hotplug/acpiphp_ibm.c
index c35e8ad..ecd4b8d 100644
--- a/drivers/pci/hotplug/acpiphp_ibm.c
+++ b/drivers/pci/hotplug/acpiphp_ibm.c
@@ -450,7 +450,7 @@ static int __init ibm_acpiphp_init(void)
 	}
 	if (acpiphp_register_attention(&ibm_attention_info)) {
 		retval = -ENODEV;
-		goto init_return;
+		goto put_device;
 	}
 
 	ibm_note.device = device;
@@ -471,6 +471,8 @@ static int __init ibm_acpiphp_init(void)
 
 init_cleanup:
 	acpiphp_unregister_attention(&ibm_attention_info);
+put_device:
+	acpi_device_put(device);
 init_return:
 	return retval;
 }
@@ -493,6 +495,7 @@ static void __exit ibm_acpiphp_exit(void)
 		err("%s: Notification handler removal failed\n", __func__);
 	/* remove the /sys entries */
 	sysfs_remove_bin_file(sysdir, &ibm_apci_table_attr);
+	acpi_device_put(ibm_note.device);
 }
 
 module_init(ibm_acpiphp_init);
diff --git a/drivers/pci/hotplug/sgi_hotplug.c b/drivers/pci/hotplug/sgi_hotplug.c
index f64ca92..b02c99e 100644
--- a/drivers/pci/hotplug/sgi_hotplug.c
+++ b/drivers/pci/hotplug/sgi_hotplug.c
@@ -462,6 +462,8 @@ static int enable_slot(struct hotplug_slot *bss_hotplug_slot)
 				}
 			}
 		}
+
+		acpi_device_put(pdevice);
 	}
 
 	/* Call the driver for the new device */
@@ -538,8 +540,10 @@ static int disable_slot(struct hotplug_slot *bss_hotplug_slot)
 
 				ret = acpi_bus_get_device(chandle,
 							  &device);
-				if (ACPI_SUCCESS(ret))
+				if (ACPI_SUCCESS(ret)) {
 					acpi_bus_trim(device, 1);
+					acpi_device_put(device);
+				}
 			}
 		}
 
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 52daaa8..56e421a 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -782,6 +782,7 @@ static int __init setup_acpi_notify(struct ibm_struct *ibm)
 			pr_err("acpi_install_notify_handler(%s) failed: %s\n",
 			       ibm->name, acpi_format_exception(status));
 		}
+		acpi_device_put(ibm->acpi->device);
 		return -ENODEV;
 	}
 	ibm->flags.acpi_notify_installed = 1;
@@ -8464,6 +8465,7 @@ static void ibm_exit(struct ibm_struct *ibm)
 		acpi_remove_notify_handler(*ibm->acpi->handle,
 					   ibm->acpi->type,
 					   dispatch_acpi_notify);
+		acpi_device_put(ibm->acpi->device);
 		ibm->flags.acpi_notify_installed = 0;
 	}
 
diff --git a/drivers/pnp/pnpacpi/core.c b/drivers/pnp/pnpacpi/core.c
index 507a8e2..0c5c0f4 100644
--- a/drivers/pnp/pnpacpi/core.c
+++ b/drivers/pnp/pnpacpi/core.c
@@ -82,7 +82,6 @@ static int pnpacpi_get_resources(struct pnp_dev *dev)
 
 static int pnpacpi_set_resources(struct pnp_dev *dev)
 {
-	struct acpi_device *acpi_dev;
 	acpi_handle handle;
 	struct acpi_buffer buffer;
 	int ret;
@@ -90,7 +89,7 @@ static int pnpacpi_set_resources(struct pnp_dev *dev)
 	pnp_dbg(&dev->dev, "set resources\n");
 
 	handle = DEVICE_ACPI_HANDLE(&dev->dev);
-	if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
+	if (!handle || !acpi_bus_has_device(handle)) {
 		dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 		return -ENODEV;
 	}
@@ -113,14 +112,13 @@ static int pnpacpi_set_resources(struct pnp_dev *dev)
 
 static int pnpacpi_disable_resources(struct pnp_dev *dev)
 {
-	struct acpi_device *acpi_dev;
 	acpi_handle handle;
 	int ret;
 
 	dev_dbg(&dev->dev, "disable resources\n");
 
 	handle = DEVICE_ACPI_HANDLE(&dev->dev);
-	if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
+	if (!handle || !acpi_bus_has_device(handle)) {
 		dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 		return 0;
 	}
@@ -138,11 +136,10 @@ static int pnpacpi_disable_resources(struct pnp_dev *dev)
 #ifdef CONFIG_ACPI_SLEEP
 static bool pnpacpi_can_wakeup(struct pnp_dev *dev)
 {
-	struct acpi_device *acpi_dev;
 	acpi_handle handle;
 
 	handle = DEVICE_ACPI_HANDLE(&dev->dev);
-	if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
+	if (!handle || !acpi_bus_has_device(handle)) {
 		dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 		return false;
 	}
@@ -152,12 +149,11 @@ static bool pnpacpi_can_wakeup(struct pnp_dev *dev)
 
 static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state)
 {
-	struct acpi_device *acpi_dev;
 	acpi_handle handle;
 	int error = 0;
 
 	handle = DEVICE_ACPI_HANDLE(&dev->dev);
-	if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
+	if (!handle || !acpi_bus_has_device(handle)) {
 		dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 		return 0;
 	}
@@ -190,11 +186,10 @@ static int pnpacpi_suspend(struct pnp_dev *dev, pm_message_t state)
 
 static int pnpacpi_resume(struct pnp_dev *dev)
 {
-	struct acpi_device *acpi_dev;
 	acpi_handle handle = DEVICE_ACPI_HANDLE(&dev->dev);
 	int error = 0;
 
-	if (!handle || ACPI_FAILURE(acpi_bus_get_device(handle, &acpi_dev))) {
+	if (!handle || !acpi_bus_has_device(handle)) {
 		dev_dbg(&dev->dev, "ACPI device not found in %s!\n", __func__);
 		return -ENODEV;
 	}
@@ -310,10 +305,12 @@ static acpi_status __init pnpacpi_add_device_handler(acpi_handle handle,
 {
 	struct acpi_device *device;
 
-	if (!acpi_bus_get_device(handle, &device))
-		pnpacpi_add_device(device);
-	else
+	if (acpi_bus_get_device(handle, &device))
 		return AE_CTRL_DEPTH;
+
+	pnpacpi_add_device(device);
+	acpi_device_put(device);
+
 	return AE_OK;
 }
 
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index bde976e..0a109fc 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -349,8 +349,22 @@ extern void unregister_acpi_bus_notifier(struct notifier_block *nb);
  * External Functions
  */
 
+static inline struct acpi_device *acpi_device_get(struct acpi_device *d)
+{
+	if (d)
+		get_device(&d->dev);
+
+	return d;
+}
+
+static inline void acpi_device_put(struct acpi_device *d)
+{
+	if (d)
+		put_device(&d->dev);
+}
+
+bool acpi_bus_has_device(acpi_handle handle);
 int acpi_bus_get_device(acpi_handle handle, struct acpi_device **device);
-void acpi_bus_data_handler(acpi_handle handle, void *context);
 acpi_status acpi_bus_get_status_handle(acpi_handle handle,
 				       unsigned long long *sta);
 int acpi_bus_get_status(struct acpi_device *device);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 06/28] ACPIHP: introduce interfaces to scan and walk ACPI devices connecting to a slot
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (4 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 05/28] ACPI: introduce interfaces to manage ACPI device reference count Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 07/28] ACPIHP: use klist to manage " Jiang Liu
                   ` (22 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci, Gaohuai Han

This patch enhances acpi_bus_scan() to implement acpihp_add_devices(),
which creates ACPI devices for hot-added system devices connecting to
an ACPI hotplug slot, but don't cross the slot boundary.

It also introduces a new interface to walk all ACPI devices connecting
to an ACPI hotplug slot.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
Signed-off-by: Hanjun Guo <guohanjun@huawei.com>
Signed-off-by: Gaohuai Han <hangaohuai@huawei.com>
---
 drivers/acpi/hotplug/Makefile |    2 +-
 drivers/acpi/hotplug/acpihp.h |    1 +
 drivers/acpi/hotplug/device.c |  123 +++++++++++++++++++++++++++++++++++++++++
 drivers/acpi/internal.h       |    2 +
 drivers/acpi/scan.c           |   11 +++-
 include/acpi/acpi_bus.h       |    2 +
 include/acpi/acpi_hotplug.h   |   15 +++++
 7 files changed, 154 insertions(+), 2 deletions(-)
 create mode 100644 drivers/acpi/hotplug/device.c

diff --git a/drivers/acpi/hotplug/Makefile b/drivers/acpi/hotplug/Makefile
index c19b350..6e5daf6 100644
--- a/drivers/acpi/hotplug/Makefile
+++ b/drivers/acpi/hotplug/Makefile
@@ -3,7 +3,7 @@
 #
 
 obj-$(CONFIG_ACPI_HOTPLUG)			+= acpihp.o
-acpihp-y					= core.o
+acpihp-y					= core.o device.o
 
 obj-$(CONFIG_ACPI_HOTPLUG_SLOT)			+= acpihp_slot.o
 acpihp_slot-y					= slot.o
diff --git a/drivers/acpi/hotplug/acpihp.h b/drivers/acpi/hotplug/acpihp.h
index 7c49eab..54d9d95 100644
--- a/drivers/acpi/hotplug/acpihp.h
+++ b/drivers/acpi/hotplug/acpihp.h
@@ -26,6 +26,7 @@
 #include <acpi/acpi.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_hotplug.h>
+#include "../internal.h"
 
 extern struct acpi_device *acpi_root;
 extern struct acpihp_slot_ops acpihp_slot_ej0;
diff --git a/drivers/acpi/hotplug/device.c b/drivers/acpi/hotplug/device.c
new file mode 100644
index 0000000..fb0451f
--- /dev/null
+++ b/drivers/acpi/hotplug/device.c
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2012 Huawei Tech. Co., Ltd.
+ * Copyright (C) 2012 Jiang Liu <jiang.liu@huawei.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/types.h>
+#include <linux/device.h>
+#include <acpi/acpi.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_hotplug.h>
+#include "acpihp.h"
+
+/*
+ * When creating ACPI devices for hot-added system devices connecting to slot,
+ * don't cross the slot boundary. Otherwise it will cause inconsistence
+ * to other hotplug slots.
+ */
+static acpi_status acpihp_filter_device(acpi_handle hdl, u32 level, void *arg)
+{
+	/* Skip if the handle corresponds to a child hotplug slot. */
+	if (level > 0 && acpihp_is_slot(hdl))
+		return AE_CTRL_DEPTH;
+
+	return AE_OK;
+}
+
+/* Create ACPI devices for hot-added system devices connecting to a slot. */
+int acpihp_add_devices(acpi_handle handle, struct acpi_device **child)
+{
+	struct acpi_bus_ops ops;
+
+	memset(&ops, 0, sizeof(ops));
+	ops.acpi_op_add = 1;
+	ops.acpi_op_filter_fn = &acpihp_filter_device;
+	ops.acpi_op_filter_arg = NULL;
+
+	return acpi_bus_scan(handle, &ops, child);
+}
+EXPORT_SYMBOL_GPL(acpihp_add_devices);
+
+struct acpihp_walk_arg {
+	acpihp_walk_device_cb cb;
+	void *arg;
+	acpi_status status;
+};
+
+static int acpihp_walk_cb(struct device *dev, void *data)
+{
+	acpi_status status;
+	struct acpihp_walk_arg *argp = (struct acpihp_walk_arg *)data;
+	struct acpi_device *acpi_dev;
+
+	if (dev->bus != &acpi_bus_type)
+		return 0;
+
+	/* Skip if the handle corresponds to a child hotplug slot. */
+	acpi_dev = container_of(dev, struct acpi_device, dev);
+	if (acpihp_is_slot(acpi_dev->handle))
+		return 0;
+
+	status = argp->cb(acpi_dev, argp->arg);
+	if (status == AE_OK) {
+		return device_for_each_child(dev, data, &acpihp_walk_cb);
+	} else if (status == AE_CTRL_DEPTH || status == AE_CTRL_SKIP) {
+		return 0;
+	} else {
+		argp->status = status;
+		return -1;
+	}
+}
+
+/*
+ * Walk all ACPI devices connecting to a hotplug slot, but don't cross the
+ * hotplug slot boundary.
+ */
+int acpihp_walk_devices(acpi_handle handle, acpihp_walk_device_cb cb,
+			void *argp)
+{
+	acpi_status status;
+	struct acpi_device *device;
+	struct acpihp_walk_arg arg;
+
+	if (acpi_bus_get_device(handle, &device))
+		return -ENODEV;
+
+	status = (*cb)(device, argp);
+	if (ACPI_SUCCESS(status)) {
+		arg.cb = cb;
+		arg.arg = argp;
+		arg.status = AE_OK;
+		(void) device_for_each_child(&device->dev, &arg,
+					     &acpihp_walk_cb);
+		status = arg.status;
+	}
+
+	if (status == AE_CTRL_DEPTH || status == AE_CTRL_TERMINATE ||
+	    status == AE_CTRL_SKIP)
+		status = AE_OK;
+	else if (ACPI_FAILURE(status))
+		ACPIHP_DEBUG("fails to walk devices under %p.\n", handle);
+
+	acpi_device_put(device);
+
+	return status == AE_OK ? 0 : -ENODEV;
+}
+EXPORT_SYMBOL_GPL(acpihp_walk_devices);
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 70843c0..6f0632d 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -95,5 +95,7 @@ static inline void suspend_nvs_restore(void) {}
 
 extern struct rw_semaphore acpi_device_data_handler_sem;
 void acpi_bus_data_handler(acpi_handle handle, void *context);
+int acpi_bus_scan(acpi_handle handle, struct acpi_bus_ops *ops,
+		  struct acpi_device **child);
 
 #endif /* _ACPI_INTERNAL_H_ */
diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c
index a1d5188..028294d 100644
--- a/drivers/acpi/scan.c
+++ b/drivers/acpi/scan.c
@@ -1431,6 +1431,14 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl,
 		return AE_CTRL_DEPTH;
 	}
 
+	/* Hooks for ACPI based system device hotplug */
+	if (ops->acpi_op_filter_fn != NULL) {
+		result = ops->acpi_op_filter_fn(handle, lvl,
+						ops->acpi_op_filter_arg);
+		if (result != AE_OK)
+			return result;
+	}
+
 	/*
 	 * We may already have an acpi_device from a previous enumeration.  If
 	 * so, we needn't add it again, but we may still have to start it.
@@ -1454,7 +1462,7 @@ static acpi_status acpi_bus_check_add(acpi_handle handle, u32 lvl,
 	return AE_OK;
 }
 
-static int acpi_bus_scan(acpi_handle handle, struct acpi_bus_ops *ops,
+int acpi_bus_scan(acpi_handle handle, struct acpi_bus_ops *ops,
 			 struct acpi_device **child)
 {
 	acpi_status status;
@@ -1612,6 +1620,7 @@ int __init acpi_scan_init(void)
 	memset(&ops, 0, sizeof(ops));
 	ops.acpi_op_add = 1;
 	ops.acpi_op_start = 1;
+	ops.acpi_op_filter_fn = NULL;
 
 	result = bus_register(&acpi_bus_type);
 	if (result) {
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index 0a109fc..8136dd7 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -127,6 +127,8 @@ typedef void (*acpi_op_notify) (struct acpi_device * device, u32 event);
 struct acpi_bus_ops {
 	u32 acpi_op_add:1;
 	u32 acpi_op_start:1;
+	acpi_status (*acpi_op_filter_fn)(acpi_handle, u32, void *);
+	void *acpi_op_filter_arg;
 };
 
 struct acpi_device_ops {
diff --git a/include/acpi/acpi_hotplug.h b/include/acpi/acpi_hotplug.h
index 0b61a10..5025a16 100644
--- a/include/acpi/acpi_hotplug.h
+++ b/include/acpi/acpi_hotplug.h
@@ -175,6 +175,21 @@ extern acpi_status acpihp_slot_get_status(struct acpihp_slot *slot,
 extern acpi_status acpihp_slot_poweron(struct acpihp_slot *slot);
 extern acpi_status acpihp_slot_poweroff(struct acpihp_slot *slot);
 
+/*
+ * Add device objects for ACPI devices connecting to an ACPI hotplug slot,
+ * but don't cross the hotplug slot boundary.
+ */
+extern int acpihp_add_devices(acpi_handle handle, struct acpi_device **child);
+
+/*
+ * Walk all ACPI devices connecting to an ACPI hotplug slot,
+ * but don't cross the hotplug slot boundary.
+ */
+typedef acpi_status (*acpihp_walk_device_cb)(struct acpi_device *acpi_device,
+					     void *argp);
+extern int acpihp_walk_devices(acpi_handle handle, acpihp_walk_device_cb cb,
+			       void *argp);
+
 extern int acpihp_debug;
 
 #define ACPIHP_WARN(fmt, ...) \
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 07/28] ACPIHP: use klist to manage ACPI devices connecting to a slot
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (5 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 06/28] ACPIHP: introduce interfaces to scan and walk ACPI devices connecting to a slot Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 08/28] ACPIHP: add callbacks into acpi_device_ops to support new hotplug framework Jiang Liu
                   ` (21 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

ACPI devices connecting to an ACPI hotplug slot are divided into groups
according to device types. Those devices will be configured/unconfigured
in order of device types when hot-adding/hot-removing system devices.

For example, when hot-adding a computer node with CPUs, memory, PCI host
bridges, IOAPICs, the order optimized for performance should be:
memory -> CPU -> IOAPIC -> PCI host bridge.

It relies on klist to manage and protect ACPI devices connecting to
an ACPI hotplug slot.

Signed-off-by: Hanjun Guo <guohanjun@huawei.com>
Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
---
 drivers/acpi/hotplug/core.c |  115 +++++++++++++++++++++++++++++++++++++++++++
 include/acpi/acpi_hotplug.h |   27 ++++++++++
 2 files changed, 142 insertions(+)

diff --git a/drivers/acpi/hotplug/core.c b/drivers/acpi/hotplug/core.c
index 081b27f..1e6dbc8 100644
--- a/drivers/acpi/hotplug/core.c
+++ b/drivers/acpi/hotplug/core.c
@@ -114,6 +114,23 @@ static char *acpihp_dev_ioapic_ids[] = {
 	NULL
 };
 
+static void acpihp_dev_node_get(struct klist_node *lp)
+{
+	struct acpihp_dev_node *dp;
+
+	dp = container_of(lp, struct acpihp_dev_node, node);
+	get_device(dp->dev);
+}
+
+static void acpihp_dev_node_put(struct klist_node *lp)
+{
+	struct acpihp_dev_node *dp;
+
+	dp = container_of(lp, struct acpihp_dev_node, node);
+	put_device(dp->dev);
+	kfree(dp);
+}
+
 static void acpihp_slot_release(struct device *dev)
 {
 	struct acpihp_slot *slot = to_acpihp_slot(dev);
@@ -123,6 +140,7 @@ static void acpihp_slot_release(struct device *dev)
 
 struct acpihp_slot *acpihp_create_slot(acpi_handle handle, char *name)
 {
+	int i;
 	struct acpihp_slot *slot;
 
 	if (name && strlen(name) >= ACPIHP_SLOT_NAME_MAX_SIZE) {
@@ -139,6 +157,9 @@ struct acpihp_slot *acpihp_create_slot(acpi_handle handle, char *name)
 	slot->handle = handle;
 	INIT_LIST_HEAD(&slot->slot_list);
 	INIT_LIST_HEAD(&slot->drvdata_list);
+	for (i = ACPIHP_DEV_TYPE_UNKNOWN; i < ACPIHP_DEV_TYPE_MAX; i++)
+		klist_init(&slot->dev_lists[i],
+			   &acpihp_dev_node_get, &acpihp_dev_node_put);
 	if (name)
 		strncpy(slot->name, name, sizeof(slot->name) - 1);
 	mutex_init(&slot->slot_mutex);
@@ -402,6 +423,100 @@ acpi_status acpihp_slot_poweroff(struct acpihp_slot *slot)
 }
 EXPORT_SYMBOL_GPL(acpihp_slot_poweroff);
 
+/* Insert an ACPI device onto a hotplug slot's device list. */
+int acpihp_slot_add_device(struct acpihp_slot *slot, enum acpihp_dev_type type,
+			   enum acpihp_dev_state state, struct device *dev)
+{
+	struct acpihp_dev_node *np;
+
+	if (type < ACPIHP_DEV_TYPE_UNKNOWN || type >= ACPIHP_DEV_TYPE_MAX) {
+		ACPIHP_DEBUG("device type %d is invalid.\n", type);
+		return -EINVAL;
+	} else if (slot == NULL) {
+		ACPIHP_DEBUG("invalid parameter, slot is NULL.\n");
+		return -EINVAL;
+	} else if (dev == NULL) {
+		ACPIHP_SLOT_DEBUG(slot, "invalid parameter, dev is NULL.\n");
+		return -EINVAL;
+	}
+
+	np = kzalloc(sizeof(*np), GFP_KERNEL);
+	if (np == NULL) {
+		ACPIHP_SLOT_WARN(slot, "fails to allocate memory.\n");
+		return -ENOMEM;
+	}
+
+	np->dev = dev;
+	np->state = state;
+	mutex_init(&np->lock);
+	klist_add_tail(&np->node, &slot->dev_lists[type]);
+	ACPIHP_SLOT_DEBUG(slot, "add device %s to klist.\n", dev_name(dev));
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(acpihp_slot_add_device);
+
+/* Remove an ACPI device from a hotplug slot's device list. */
+int acpihp_slot_remove_device(struct acpihp_slot *slot,
+			      enum acpihp_dev_type type, struct device *dev)
+{
+	int ret = -ENOENT;
+	struct klist_iter iter;
+	struct klist_node *ip;
+	struct acpihp_dev_node *np;
+
+	if (type < ACPIHP_DEV_TYPE_UNKNOWN || type >= ACPIHP_DEV_TYPE_MAX) {
+		ACPIHP_DEBUG("device type %d is invalid.\n", type);
+		return -EINVAL;
+	} else if (slot == NULL) {
+		ACPIHP_DEBUG("invalid parameter, slot is NULL.\n");
+		return -EINVAL;
+	} else if (dev == NULL) {
+		ACPIHP_SLOT_DEBUG(slot, "invalid parameter, dev is NULL.\n");
+		return -EINVAL;
+	}
+
+	klist_iter_init(&slot->dev_lists[type], &iter);
+	while ((ip = klist_next(&iter)) != NULL) {
+		np = container_of(ip, struct acpihp_dev_node, node);
+		if (np->dev == dev) {
+			ACPIHP_SLOT_DEBUG(slot,
+					  "remove device %s from klist.\n",
+					  dev_name(dev));
+			klist_del(&np->node);
+			ret = 0;
+			break;
+		}
+	}
+	klist_iter_exit(&iter);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(acpihp_slot_remove_device);
+
+/* Remove all ACPI devices from the list */
+int acpihp_remove_device_list(struct klist *dev_list)
+{
+	struct klist_iter iter;
+	struct klist_node *ip;
+	struct acpihp_dev_node *np;
+
+	if (dev_list == NULL) {
+		ACPIHP_DEBUG("invalid parameter, dev_list is NULL.\n");
+		return -EINVAL;
+	}
+
+	klist_iter_init(dev_list, &iter);
+	while ((ip = klist_next(&iter)) != NULL) {
+		np = container_of(ip, struct acpihp_dev_node, node);
+		klist_del(&np->node);
+	}
+	klist_iter_exit(&iter);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(acpihp_remove_device_list);
+
 /* SYSFS interfaces */
 static ssize_t acpihp_slot_object_show(struct device *d,
 		struct device_attribute *attr, char *buf)
diff --git a/include/acpi/acpi_hotplug.h b/include/acpi/acpi_hotplug.h
index 5025a16..2e7011e 100644
--- a/include/acpi/acpi_hotplug.h
+++ b/include/acpi/acpi_hotplug.h
@@ -47,6 +47,24 @@ enum acpihp_dev_type {
 	ACPIHP_DEV_TYPE_MAX
 };
 
+enum acpihp_dev_state {
+	DEVICE_STATE_UNKOWN = 0x00,
+	DEVICE_STATE_CONNECTED,
+	DEVICE_STATE_PRE_CONFIGURE,
+	DEVICE_STATE_CONFIGURED,
+	DEVICE_STATE_PRE_RELEASE,
+	DEVICE_STATE_RELEASED,
+	DEVICE_STATE_PRE_UNCONFIGURE,
+	DEVICE_STATE_MAX
+};
+
+struct acpihp_dev_node {
+	struct device		*dev;
+	enum acpihp_dev_state	state;
+	struct mutex		lock;
+	struct klist_node	node;
+};
+
 /*
  * ACPI hotplug slot is an abstraction of receptacles where a group of
  * system devices could be attached, just like PCI slot in PCI hotplug.
@@ -190,6 +208,15 @@ typedef acpi_status (*acpihp_walk_device_cb)(struct acpi_device *acpi_device,
 extern int acpihp_walk_devices(acpi_handle handle, acpihp_walk_device_cb cb,
 			       void *argp);
 
+extern int acpihp_slot_add_device(struct acpihp_slot *slot,
+				  enum acpihp_dev_type type,
+				  enum acpihp_dev_state state,
+				  struct device *dev);
+extern int acpihp_slot_remove_device(struct acpihp_slot *slot,
+				     enum acpihp_dev_type type,
+				     struct device *dev);
+extern int acpihp_remove_device_list(struct klist *dev_list);
+
 extern int acpihp_debug;
 
 #define ACPIHP_WARN(fmt, ...) \
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 08/28] ACPIHP: add callbacks into acpi_device_ops to support new hotplug framework
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (6 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 07/28] ACPIHP: use klist to manage " Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 09/28] ACPIHP: provide interfaces to associate driver specific data with slots Jiang Liu
                   ` (20 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci, Gaohuai Han

Add new callbacks into struct acpi_device_ops to provide better error handling,
error recover and operation cancellation for ACPI based system device hotplug.

There are three major operations and each major operation is divided into
three steps.
1) pre_configure, configure, post_configure
	add an ACPI device into running system and rollback if error happens
	cancelled by user.
2) pre_release, release, post_release
	reclaim an ACPI device from running system and rollback if error
	happens or cancelled by user. It's important to privode a mechanism
	to cancel memory hot-removal operations because it's may take very
	long or even endless time to reclaim a memory device.
3) pre_unconfigure, unconfigure, post_unconfigure
	remove an ACPI device from running system and release all resources
	associated with it.

There's another callback to query status and information about the ACPI device.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
Signed-off-by: Hanjun Guo <guohanjun@huawei.com>
Signed-off-by: Gaohuai Han <hangaohuai@huawei.com>
---
 drivers/acpi/hotplug/device.c |   93 +++++++++++++++++++++++++++++++++++++++++
 include/acpi/acpi_bus.h       |    3 ++
 include/acpi/acpi_hotplug.h   |   64 ++++++++++++++++++++++++++++
 3 files changed, 160 insertions(+)

diff --git a/drivers/acpi/hotplug/device.c b/drivers/acpi/hotplug/device.c
index fb0451f..d566522 100644
--- a/drivers/acpi/hotplug/device.c
+++ b/drivers/acpi/hotplug/device.c
@@ -1,6 +1,7 @@
 /*
  * Copyright (C) 2012 Huawei Tech. Co., Ltd.
  * Copyright (C) 2012 Jiang Liu <jiang.liu@huawei.com>
+ * Copyright (C) 2012 Hanjun Guo <guohanjun@huawei.com>
  *
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  *
@@ -27,6 +28,98 @@
 #include <acpi/acpi_hotplug.h>
 #include "acpihp.h"
 
+int acpihp_dev_get_info(struct acpi_device *device,
+			struct acpihp_dev_info *info)
+{
+	int ret = -ENOSYS;
+
+	acpihp_dev_get_type(device->handle, &info->type);
+
+	device_lock(&device->dev);
+	if (device->driver && device->driver->ops.hp_ops &&
+	    device->driver->ops.hp_ops->get_info)
+		ret = device->driver->ops.hp_ops->get_info(device, info);
+	else
+#if 0
+		/* Turn on this once all system devices have been converted
+		 * to the new hotplug framework
+		 */
+		info->status |= ACPIHP_DEV_STATUS_IRREMOVABLE;
+#else
+		ret = 0;
+#endif
+
+	if (device->driver)
+		info->status |= ACPIHP_DEV_STATUS_ATTACHED;
+	device_unlock(&device->dev);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(acpihp_dev_get_info);
+
+#define	ACPIHP_DEFINE_FUNC1(method, def, err, type) \
+int acpihp_dev_##method(struct acpi_device *device, type val) \
+{ \
+	int ret; \
+	BUG_ON(device == NULL); \
+	device_lock(&device->dev); \
+	if (!device->driver || !device->driver->ops.hp_ops) \
+		ret = (err); \
+	else if (!device->driver->ops.hp_ops->method) \
+		ret = (def); \
+	else \
+		ret = device->driver->ops.hp_ops->method(device, val); \
+	device_unlock(&device->dev); \
+	return ret; \
+} \
+EXPORT_SYMBOL_GPL(acpihp_dev_##method)
+
+#define	ACPIHP_DEFINE_FUNC2(method, def, err, type) \
+int acpihp_dev_##method(struct acpi_device *device, type val) \
+{ \
+	int ret = 0; \
+	BUG_ON(device == NULL); \
+	device_lock(&device->dev); \
+	if (!device->driver || !device->driver->ops.hp_ops) \
+		ret = (err); \
+	else if (!device->driver->ops.hp_ops->method) \
+		ret = (def); \
+	else \
+		device->driver->ops.hp_ops->method(device, val); \
+	device_unlock(&device->dev); \
+	return ret; \
+} \
+EXPORT_SYMBOL_GPL(acpihp_dev_##method)
+
+#define	ACPIHP_DEFINE_FUNC3(method, def, err) \
+int acpihp_dev_##method(struct acpi_device *device) \
+{ \
+	int ret = 0; \
+	BUG_ON(device == NULL); \
+	device_lock(&device->dev); \
+	if (!device->driver || !device->driver->ops.hp_ops) \
+		ret = (err); \
+	else if (!device->driver->ops.hp_ops->method) \
+		ret = (def); \
+	else \
+		device->driver->ops.hp_ops->method(device);\
+	device_unlock(&device->dev); \
+	return ret; \
+} \
+EXPORT_SYMBOL_GPL(acpihp_dev_##method)
+
+ACPIHP_DEFINE_FUNC1(pre_configure, 0, 0, struct acpihp_cancel_context *);
+ACPIHP_DEFINE_FUNC1(configure, 0, -ENOSYS, struct acpihp_cancel_context *);
+ACPIHP_DEFINE_FUNC2(post_configure, 0, 0, enum acpihp_dev_post_cmd);
+
+ACPIHP_DEFINE_FUNC1(pre_release, 0, 0, struct acpihp_cancel_context *);
+ACPIHP_DEFINE_FUNC1(release, 0, 0, struct acpihp_cancel_context *);
+ACPIHP_DEFINE_FUNC2(post_release, 0, 0, enum acpihp_dev_post_cmd);
+
+ACPIHP_DEFINE_FUNC3(pre_unconfigure, 0, 0);
+ACPIHP_DEFINE_FUNC3(unconfigure, 0, -ENOSYS);
+ACPIHP_DEFINE_FUNC3(post_unconfigure, 0, 0);
+
 /*
  * When creating ACPI devices for hot-added system devices connecting to slot,
  * don't cross the slot boundary. Otherwise it will cause inconsistence
diff --git a/include/acpi/acpi_bus.h b/include/acpi/acpi_bus.h
index 8136dd7..6f9fdf2 100644
--- a/include/acpi/acpi_bus.h
+++ b/include/acpi/acpi_bus.h
@@ -138,6 +138,9 @@ struct acpi_device_ops {
 	acpi_op_bind bind;
 	acpi_op_unbind unbind;
 	acpi_op_notify notify;
+#ifdef	CONFIG_ACPI_HOTPLUG
+	struct acpihp_dev_ops *hp_ops;
+#endif	/* CONFIG_ACPI_HOTPLUG */
 };
 
 #define ACPI_DRIVER_ALL_NOTIFY_EVENTS	0x1	/* system AND device events */
diff --git a/include/acpi/acpi_hotplug.h b/include/acpi/acpi_hotplug.h
index 2e7011e..fd46997 100644
--- a/include/acpi/acpi_hotplug.h
+++ b/include/acpi/acpi_hotplug.h
@@ -65,6 +65,51 @@ struct acpihp_dev_node {
 	struct klist_node	node;
 };
 
+/* Status of system devices. */
+#define	ACPIHP_DEV_STATUS_ATTACHED	0x1 /* Device driver attached */
+#define	ACPIHP_DEV_STATUS_STARTED	0x2 /* Device started */
+#define	ACPIHP_DEV_STATUS_IRREMOVABLE	0x10000 /* Device can't be removed */
+#define	ACPIHP_DEV_STATUS_FAULT		0x20000 /* Device in fault status */
+
+struct acpihp_dev_info {
+	enum acpihp_dev_type		type;
+	uint32_t			status;
+};
+
+/* Rollback or commit changes in post_{confiure|release} */
+enum acpihp_dev_post_cmd {
+	ACPIHP_DEV_POST_CMD_ROLLBACK,
+	ACPIHP_DEV_POST_CMD_COMMIT
+};
+
+/*
+ * ACPI system device drivers may check cancellations of hotplug operations
+ * by invoking the callback.
+ */
+struct acpihp_cancel_context {
+	int (*check_cancel)(struct acpihp_cancel_context *ctx);
+};
+
+/*
+ * Callback hooks provided by ACPI device drivers to support system device
+ * hotplug. To support hotplug, an ACPI system device driver should implement
+ * configure(), unconfigure() and get_info() at minimal.
+ */
+struct acpihp_dev_ops {
+	int (*get_info)(struct acpi_device *, struct acpihp_dev_info *info);
+	int (*pre_configure)(struct acpi_device *,
+			     struct acpihp_cancel_context *);
+	int (*configure)(struct acpi_device *, struct acpihp_cancel_context *);
+	void (*post_configure)(struct acpi_device *, enum acpihp_dev_post_cmd);
+	int (*pre_release)(struct acpi_device *,
+			   struct acpihp_cancel_context *);
+	int (*release)(struct acpi_device *, struct acpihp_cancel_context *);
+	void (*post_release)(struct acpi_device *, enum acpihp_dev_post_cmd);
+	void (*pre_unconfigure)(struct acpi_device *);
+	void (*unconfigure)(struct acpi_device *);
+	void (*post_unconfigure)(struct acpi_device *);
+};
+
 /*
  * ACPI hotplug slot is an abstraction of receptacles where a group of
  * system devices could be attached, just like PCI slot in PCI hotplug.
@@ -166,6 +211,25 @@ extern int acpihp_register_class(void);
 /* Unregister the ACPI hotplug slot class driver */
 extern void acpihp_unregister_class(void);
 
+/* Interfaces to invoke the ACPI device driver's hotplug hooks. */
+extern int acpihp_dev_get_info(struct acpi_device *device,
+			       struct acpihp_dev_info *info);
+extern int acpihp_dev_pre_configure(struct acpi_device *device,
+				    struct acpihp_cancel_context *ctx);
+extern int acpihp_dev_configure(struct acpi_device *device,
+				struct acpihp_cancel_context *ctx);
+extern int acpihp_dev_post_configure(struct acpi_device *device,
+				     enum acpihp_dev_post_cmd cmd);
+extern int acpihp_dev_pre_release(struct acpi_device *device,
+				  struct acpihp_cancel_context *ctx);
+extern int acpihp_dev_release(struct acpi_device *device,
+			      struct acpihp_cancel_context *ctx);
+extern int acpihp_dev_post_release(struct acpi_device *device,
+				   enum acpihp_dev_post_cmd cmd);
+extern int acpihp_dev_pre_unconfigure(struct acpi_device *device);
+extern int acpihp_dev_unconfigure(struct acpi_device *device);
+extern int acpihp_dev_post_unconfigure(struct acpi_device *device);
+
 /* Utility routines */
 extern int acpihp_dev_get_type(acpi_handle handle, enum acpihp_dev_type *type);
 extern bool acpihp_dev_match_ids(struct acpi_device_info *infop, char **ids);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 09/28] ACPIHP: provide interfaces to associate driver specific data with slots
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (7 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 08/28] ACPIHP: add callbacks into acpi_device_ops to support new hotplug framework Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 10/28] ACPIHP: implement utility functions to support system device hotplug Jiang Liu
                   ` (19 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci, Gaohuai Han

This patch implements interfaces to attach/detach/get driver specific data
to/from ACPI hotplug slots.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
Signed-off-by: Gaohuai Han <hangaohuai@huawei.com>
---
 drivers/acpi/hotplug/core.c |   88 +++++++++++++++++++++++++++++++++++++++++++
 include/acpi/acpi_hotplug.h |    8 ++++
 2 files changed, 96 insertions(+)

diff --git a/drivers/acpi/hotplug/core.c b/drivers/acpi/hotplug/core.c
index 1e6dbc8..ffd3b54 100644
--- a/drivers/acpi/hotplug/core.c
+++ b/drivers/acpi/hotplug/core.c
@@ -35,9 +35,16 @@
 #include <acpi/acpi_hotplug.h>
 #include "acpihp.h"
 
+struct acpihp_drv_data {
+	struct list_head		node;
+	struct class_interface		*key;
+	void				*data;
+};
+
 #define to_acpihp_slot(d) container_of(d, struct acpihp_slot, dev)
 
 static DEFINE_MUTEX(acpihp_mutex);
+static DEFINE_MUTEX(acpihp_drvdata_mutex);
 static int acpihp_class_count;
 static struct kset *acpihp_slot_kset;
 
@@ -367,6 +374,87 @@ char *acpihp_get_slot_type_name(enum acpihp_slot_type type)
 }
 EXPORT_SYMBOL_GPL(acpihp_get_slot_type_name);
 
+int acpihp_slot_attach_drv_data(struct acpihp_slot *slot,
+				struct class_interface *drv, void *data)
+{
+	struct acpihp_drv_data *dp, *cp;
+
+	if (slot == NULL || drv == NULL) {
+		ACPIHP_DEBUG("invalid parameters.\n");
+		return -EINVAL;
+	}
+
+	dp = kzalloc(sizeof(*dp), GFP_KERNEL);
+	if (dp == NULL)
+		return -ENOMEM;
+
+	INIT_LIST_HEAD(&dp->node);
+	dp->key = drv;
+	dp->data = data;
+
+	mutex_lock(&acpihp_drvdata_mutex);
+	list_for_each_entry(cp, &slot->drvdata_list, node)
+		if (cp->key == drv) {
+			mutex_unlock(&acpihp_drvdata_mutex);
+			kfree(dp);
+			return -EEXIST;
+		}
+	list_add(&dp->node, &slot->drvdata_list);
+	mutex_unlock(&acpihp_drvdata_mutex);
+
+	return 0;
+}
+EXPORT_SYMBOL_GPL(acpihp_slot_attach_drv_data);
+
+int acpihp_slot_detach_drv_data(struct acpihp_slot *slot,
+				struct class_interface *drv, void **data)
+{
+	struct acpihp_drv_data *cp;
+
+	if (slot == NULL || drv == NULL || data == NULL) {
+		ACPIHP_DEBUG("invalid parameters.\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&acpihp_drvdata_mutex);
+	list_for_each_entry(cp, &slot->drvdata_list, node)
+		if (cp->key == drv) {
+			list_del(&cp->node);
+			*data = cp->data;
+			mutex_unlock(&acpihp_drvdata_mutex);
+			kfree(cp);
+			return 0;
+		}
+	mutex_unlock(&acpihp_drvdata_mutex);
+
+	return -ENOENT;
+}
+EXPORT_SYMBOL_GPL(acpihp_slot_detach_drv_data);
+
+int acpihp_slot_get_drv_data(struct acpihp_slot *slot,
+			     struct class_interface *drv, void **data)
+{
+	int ret = -ENOENT;
+	struct acpihp_drv_data *cp;
+
+	if (slot == NULL || drv == NULL || data == NULL) {
+		ACPIHP_DEBUG("invalid parameters.\n");
+		return -EINVAL;
+	}
+
+	mutex_lock(&acpihp_drvdata_mutex);
+	list_for_each_entry(cp, &slot->drvdata_list, node)
+		if (cp->key == drv) {
+			*data = cp->data;
+			ret = 0;
+			break;
+		}
+	mutex_unlock(&acpihp_drvdata_mutex);
+
+	return ret;
+}
+EXPORT_SYMBOL_GPL(acpihp_slot_get_drv_data);
+
 /*
  * slot_ops should be valid during the life cycle of a slot, so no protection.
  */
diff --git a/include/acpi/acpi_hotplug.h b/include/acpi/acpi_hotplug.h
index fd46997..97febcb 100644
--- a/include/acpi/acpi_hotplug.h
+++ b/include/acpi/acpi_hotplug.h
@@ -257,6 +257,14 @@ extern acpi_status acpihp_slot_get_status(struct acpihp_slot *slot,
 extern acpi_status acpihp_slot_poweron(struct acpihp_slot *slot);
 extern acpi_status acpihp_slot_poweroff(struct acpihp_slot *slot);
 
+/* Help routines to associate driver data with hotplug slots. */
+extern int acpihp_slot_attach_drv_data(struct acpihp_slot *slot,
+			struct class_interface *drv, void *data);
+extern int acpihp_slot_detach_drv_data(struct acpihp_slot *slot,
+			struct class_interface *drv, void **data);
+extern int acpihp_slot_get_drv_data(struct acpihp_slot *slot,
+			struct class_interface *drv, void **data);
+
 /*
  * Add device objects for ACPI devices connecting to an ACPI hotplug slot,
  * but don't cross the hotplug slot boundary.
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 10/28] ACPIHP: implement utility functions to support system device hotplug
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (8 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 09/28] ACPIHP: provide interfaces to associate driver specific data with slots Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 11/28] ACPIHP: implement skeleton of ACPI system device hotplug driver Jiang Liu
                   ` (18 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

This patch implements some utility functions to support system device hotplug.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
Signed-off-by: Hanjun Guo <guohanjun@huawei.com>
---
 drivers/acpi/hotplug/core.c |   77 +++++++++++++++++++++++++++++++++++++++++++
 include/acpi/acpi_hotplug.h |    9 +++++
 2 files changed, 86 insertions(+)

diff --git a/drivers/acpi/hotplug/core.c b/drivers/acpi/hotplug/core.c
index ffd3b54..ffc9e40 100644
--- a/drivers/acpi/hotplug/core.c
+++ b/drivers/acpi/hotplug/core.c
@@ -605,6 +605,83 @@ int acpihp_remove_device_list(struct klist *dev_list)
 }
 EXPORT_SYMBOL_GPL(acpihp_remove_device_list);
 
+bool acpihp_slot_present(struct acpihp_slot *slot)
+{
+	acpi_status status;
+	unsigned long long sta;
+
+	status = acpihp_slot_get_status(slot, &sta);
+	if (ACPI_FAILURE(status)) {
+		ACPIHP_SLOT_WARN(slot, "fails to get status.\n");
+		return false;
+	}
+
+	return !!(sta & ACPI_STA_DEVICE_PRESENT);
+}
+EXPORT_SYMBOL_GPL(acpihp_slot_present);
+
+bool acpihp_slot_powered(struct acpihp_slot *slot)
+{
+	acpi_status status;
+	unsigned long long sta;
+
+	/* hotplug slot must implement _STA method */
+	status = acpihp_slot_get_status(slot, &sta);
+	if (ACPI_FAILURE(status)) {
+		ACPIHP_SLOT_WARN(slot, "fails to get status.\n");
+		return false;
+	}
+
+	if ((sta & ACPI_STA_DEVICE_PRESENT) &&
+	    ((sta & ACPI_STA_DEVICE_ENABLED) ||
+	    (sta & ACPI_STA_DEVICE_FUNCTIONING)))
+		return true;
+
+	return false;
+}
+EXPORT_SYMBOL_GPL(acpihp_slot_powered);
+
+void acpihp_slot_set_flag(struct acpihp_slot *slot, u32 flags)
+{
+	mutex_lock(&slot->slot_mutex);
+	slot->flags |= flags;
+	mutex_unlock(&slot->slot_mutex);
+}
+EXPORT_SYMBOL_GPL(acpihp_slot_set_flag);
+
+void acpihp_slot_clear_flag(struct acpihp_slot *slot, u32 flags)
+{
+	mutex_lock(&slot->slot_mutex);
+	slot->flags &= ~flags;
+	mutex_unlock(&slot->slot_mutex);
+}
+EXPORT_SYMBOL_GPL(acpihp_slot_clear_flag);
+
+u32 acpihp_slot_get_flag(struct acpihp_slot *slot, u32 flags)
+{
+	mutex_lock(&slot->slot_mutex);
+	flags &= slot->flags;
+	mutex_unlock(&slot->slot_mutex);
+
+	return flags;
+}
+EXPORT_SYMBOL_GPL(acpihp_slot_get_flag);
+
+void acpihp_slot_change_state(struct acpihp_slot *slot,
+			      enum acpihp_slot_state state)
+{
+	if (state < ACPIHP_SLOT_STATE_UNKNOWN ||
+	    state > ACPIHP_SLOT_STATE_MAX) {
+		ACPIHP_SLOT_WARN(slot, "slot state %d is invalid.\n", state);
+		BUG_ON(state);
+	}
+
+	mutex_lock(&slot->slot_mutex);
+	slot->state = state;
+	mutex_unlock(&slot->slot_mutex);
+}
+EXPORT_SYMBOL_GPL(acpihp_slot_change_state);
+
 /* SYSFS interfaces */
 static ssize_t acpihp_slot_object_show(struct device *d,
 		struct device_attribute *attr, char *buf)
diff --git a/include/acpi/acpi_hotplug.h b/include/acpi/acpi_hotplug.h
index 97febcb..0506f73 100644
--- a/include/acpi/acpi_hotplug.h
+++ b/include/acpi/acpi_hotplug.h
@@ -289,6 +289,15 @@ extern int acpihp_slot_remove_device(struct acpihp_slot *slot,
 				     struct device *dev);
 extern int acpihp_remove_device_list(struct klist *dev_list);
 
+/* Utility Functions */
+extern bool acpihp_slot_present(struct acpihp_slot *slot);
+extern bool acpihp_slot_powered(struct acpihp_slot *slot);
+extern void acpihp_slot_set_flag(struct acpihp_slot *slot, u32 flags);
+extern void acpihp_slot_clear_flag(struct acpihp_slot *slot, u32 flags);
+extern u32 acpihp_slot_get_flag(struct acpihp_slot *slot, u32 flags);
+extern void acpihp_slot_change_state(struct acpihp_slot *slot,
+				     enum acpihp_slot_state state);
+
 extern int acpihp_debug;
 
 #define ACPIHP_WARN(fmt, ...) \
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 11/28] ACPIHP: implement skeleton of ACPI system device hotplug driver
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (9 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 10/28] ACPIHP: implement utility functions to support system device hotplug Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 12/28] ACPIHP: analyse dependencies among ACPI hotplug slots Jiang Liu
                   ` (17 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

This patch implements the skeleton of the ACPI based system device
hotplug driver. This driver implements a class driver and registers
it onto acpihp_slot_class to manage all ACPI hotplug slots.

This is a platform independent driver, which implements a state machine
for hotplug slot and provides sysfs user interfaces to access hotplug
functionalities. It depends on the platform specific slot enumerator
driver to control individual hotplug slot and ACPI container, processor,
memory and PCI host bridge drivers to configure/unconfigure individual
ACPI devices.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
Signed-off-by: Hanjun Guo <guohanjun@huawei.com>
---
 drivers/acpi/Kconfig              |   15 ++
 drivers/acpi/hotplug/Makefile     |    3 +
 drivers/acpi/hotplug/acpihp_drv.h |   62 +++++++
 drivers/acpi/hotplug/drv_main.c   |  346 +++++++++++++++++++++++++++++++++++++
 4 files changed, 426 insertions(+)
 create mode 100644 drivers/acpi/hotplug/acpihp_drv.h
 create mode 100644 drivers/acpi/hotplug/drv_main.c

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 5a5a3e5..1f972f0 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -357,6 +357,21 @@ config ACPI_HOTPLUG_SLOT_FAKE
 	  could be used to test hotplug functionalities on hardware platforms
 	  with out system device hotplug capabilities.
 
+config ACPI_HOTPLUG_DRIVER
+	tristate "ACPI Based System Device Hotplug Driver"
+	depends on ACPI_HOTPLUG
+	default y
+	help
+	  This driver implements a framework to manage ACPI system device
+	  hotplug slots, which could be used to support hotplug of processor,
+	  memory, PCI host bridge and computner node etc.
+
+	  It depends on ACPI container, processor, memory and PCI host bridge
+	  drivers to configure/unconfigure individual ACPI devices.
+
+	  To compile this driver as a module, choose M here:
+	  the module will be called acpihp_drv.
+
 config ACPI_CONTAINER
 	tristate "Container and Module Devices (EXPERIMENTAL)"
 	depends on EXPERIMENTAL
diff --git a/drivers/acpi/hotplug/Makefile b/drivers/acpi/hotplug/Makefile
index 6e5daf6..6257047 100644
--- a/drivers/acpi/hotplug/Makefile
+++ b/drivers/acpi/hotplug/Makefile
@@ -9,3 +9,6 @@ obj-$(CONFIG_ACPI_HOTPLUG_SLOT)			+= acpihp_slot.o
 acpihp_slot-y					= slot.o
 acpihp_slot-y					+= slot_ej0.o
 acpihp_slot-$(CONFIG_ACPI_HOTPLUG_SLOT_FAKE)	+= slot_fake.o
+
+obj-$(CONFIG_ACPI_HOTPLUG_DRIVER)		+= acpihp_drv.o
+acpihp_drv-y					= drv_main.o
diff --git a/drivers/acpi/hotplug/acpihp_drv.h b/drivers/acpi/hotplug/acpihp_drv.h
new file mode 100644
index 0000000..9b1c1c6
--- /dev/null
+++ b/drivers/acpi/hotplug/acpihp_drv.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2012 Huawei Tech. Co., Ltd.
+ * Copyright (C) 2012 Jiang Liu <jiang.liu@huawei.com>
+ * Copyright (C) 2012 Hanjun Guo <guohanjun@huawei.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#ifndef	__ACPIHP_DRV_H__
+#define	__ACPIHP_DRV_H__
+
+/* Commands to change state of a hotplug slot */
+enum acpihp_drv_cmd {
+	ACPIHP_DRV_CMD_NOOP = 0,
+	ACPIHP_DRV_CMD_POWERON = 0x1,
+	ACPIHP_DRV_CMD_CONNECT = 0x2,
+	ACPIHP_DRV_CMD_CONFIGURE = 0x4,
+	ACPIHP_DRV_CMD_UNCONFIGURE = 0x8,
+	ACPIHP_DRV_CMD_DISCONNECT = 0x10,
+	ACPIHP_DRV_CMD_POWEROFF = 0x20,
+	ACPIHP_DRV_CMD_CANCEL = 0x40,
+	ACPIHP_DRV_CMD_MAX
+};
+
+/* Hotplug operations may be triggered by firmware or OS */
+enum acpihp_dev_event {
+	ACPIHP_DRV_EVENT_FROM_FW,
+	ACPIHP_DRV_EVENT_FROM_OS
+};
+
+struct acpihp_slot_drv {
+	enum acpihp_dev_event	event_flag;
+	struct mutex		op_mutex; /* Prevent concurrent hotplugs. */
+	struct list_head	depend_list; /* Dependency relationship */
+	atomic_t		cancel_state;
+	atomic_t		cancel_users;
+	struct acpihp_cancel_context	cancel_ctx;
+};
+
+void acpihp_drv_get_data(struct acpihp_slot *slot,
+			 struct acpihp_slot_drv **data);
+int acpihp_drv_enumerate_devices(struct acpihp_slot *slot);
+void acpihp_drv_update_slot_state(struct acpihp_slot *slot);
+int acpihp_drv_update_slot_status(struct acpihp_slot *slot);
+
+#endif	/* __ACPIHP_DRV_H__ */
diff --git a/drivers/acpi/hotplug/drv_main.c b/drivers/acpi/hotplug/drv_main.c
new file mode 100644
index 0000000..3e2a52a
--- /dev/null
+++ b/drivers/acpi/hotplug/drv_main.c
@@ -0,0 +1,346 @@
+/*
+ * Copyright (C) 2012 Huawei Tech. Co., Ltd.
+ * Copyright (C) 2012 Jiang Liu <jiang.liu@huawei.com>
+ * Copyright (C) 2012 Hanjun Guo <guohanjun@huawei.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/errno.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/kthread.h>
+#include <linux/delay.h>
+#include <linux/acpi.h>
+#include <acpi/acpi_hotplug.h>
+#include "acpihp_drv.h"
+
+static struct class_interface acpihp_drv_interface;
+
+void acpihp_drv_get_data(struct acpihp_slot *slot,
+			 struct acpihp_slot_drv **data)
+{
+	*data = NULL;
+	acpihp_slot_get_drv_data(slot, &acpihp_drv_interface, (void **)data);
+}
+
+/* Update slot state according to state of devices connecting to it. */
+void acpihp_drv_update_slot_state(struct acpihp_slot *slot)
+{
+	enum acpihp_dev_type type;
+	enum acpihp_slot_state state;
+	struct klist_iter iter;
+	struct klist_node *ip;
+	struct acpihp_dev_node *dp;
+	bool connected = false;
+	bool configured = false;
+
+	if (!acpihp_slot_present(slot)) {
+		state = ACPIHP_SLOT_STATE_ABSENT;
+		goto out;
+	} else if (!acpihp_slot_powered(slot)) {
+		state = ACPIHP_SLOT_STATE_PRESENT;
+		goto out;
+	}
+
+	for (type = ACPIHP_DEV_TYPE_UNKNOWN;
+	     type < ACPIHP_DEV_TYPE_MAX && !configured;
+	     type++) {
+		klist_iter_init(&slot->dev_lists[type], &iter);
+		while ((ip = klist_next(&iter)) != NULL) {
+			connected = true;
+			dp = container_of(ip, struct acpihp_dev_node, node);
+			if (dp->state == DEVICE_STATE_CONFIGURED) {
+				configured = true;
+				break;
+			}
+		}
+		klist_iter_exit(&iter);
+	}
+
+	if (configured)
+		state = ACPIHP_SLOT_STATE_CONFIGURED;
+	else if (connected)
+		state = ACPIHP_SLOT_STATE_CONNECTED;
+	else
+		state = ACPIHP_SLOT_STATE_POWERED;
+
+out:
+	acpihp_slot_change_state(slot, state);
+}
+
+/* Update slot status according to status of devices connecting to it. */
+int acpihp_drv_update_slot_status(struct acpihp_slot *slot)
+{
+	int ret = 0;
+	enum acpihp_dev_type type;
+	struct klist_iter iter;
+	struct klist_node *ip;
+	struct acpihp_dev_node *np;
+	struct acpi_device *dev;
+	struct acpihp_dev_info *info;
+
+	if (!slot)
+		return -EINVAL;
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (!info)
+		return -ENOMEM;
+
+	for (type = ACPIHP_DEV_TYPE_CONTAINER; type <= ACPIHP_DEV_TYPE_IOAPIC;
+	     type++) {
+		klist_iter_init(&slot->dev_lists[type], &iter);
+		while ((ip = klist_next(&iter)) != NULL) {
+			np = container_of(ip, struct acpihp_dev_node, node);
+			dev = container_of(np->dev, struct acpi_device, dev);
+			ret = acpihp_dev_get_info(dev, info);
+			if (ret) {
+				ACPIHP_SLOT_DEBUG(slot,
+					"fails to get info about %s.\n",
+					dev_name(&dev->dev));
+				klist_iter_exit(&iter);
+				goto out;
+			}
+
+			if (info->status & ACPIHP_DEV_STATUS_FAULT)
+				acpihp_slot_set_flag(slot,
+						ACPIHP_SLOT_FLAG_FAULT);
+			if (info->status & ACPIHP_DEV_STATUS_IRREMOVABLE)
+				acpihp_slot_set_flag(slot,
+						ACPIHP_SLOT_FLAG_IRREMOVABLE);
+		}
+		klist_iter_exit(&iter);
+	}
+
+out:
+	kfree(info);
+
+	return ret;
+}
+EXPORT_SYMBOL(acpihp_drv_update_slot_status);
+
+/* Add ACPI device to hotplug slot's device list */
+static acpi_status acpihp_drv_enum_device(struct acpi_device *dev, void *argp)
+{
+	int ret = -ENOMEM;
+	acpi_status rv = AE_ERROR;
+	enum acpihp_dev_type type;
+	enum acpihp_dev_state state;
+	struct acpihp_dev_info *info;
+	struct acpihp_slot *slot = (struct acpihp_slot *)argp;
+
+	if (acpihp_dev_get_type(dev->handle, &type)) {
+		ACPIHP_SLOT_DEBUG(slot, "fails to get device type of %s.\n",
+				  dev_name(&dev->dev));
+		return AE_ERROR;
+	} else if (type == ACPIHP_DEV_TYPE_MAX) {
+		/*
+		 * Some ACPI objects for IO devices, such as PCI/IDE etc, only
+		 * implement _ADR instead of _HID/_CID, skip them.
+		 */
+		return AE_CTRL_DEPTH;
+	}
+
+	info = kzalloc(sizeof(*info), GFP_KERNEL);
+	if (info)
+		ret = acpihp_dev_get_info(dev, info);
+
+	if (!ret) {
+		if (info->status & ACPIHP_DEV_STATUS_STARTED)
+			state = DEVICE_STATE_CONFIGURED;
+		else
+			state = DEVICE_STATE_CONNECTED;
+
+		if (info->status & ACPIHP_DEV_STATUS_IRREMOVABLE)
+			acpihp_slot_set_flag(slot,
+					     ACPIHP_SLOT_FLAG_IRREMOVABLE);
+		if (info->status & ACPIHP_DEV_STATUS_FAULT)
+			acpihp_slot_set_flag(slot, ACPIHP_SLOT_FLAG_FAULT);
+
+		if (acpihp_slot_add_device(slot, type, state, &dev->dev)) {
+			ACPIHP_SLOT_DEBUG(slot, "fails to add device %s.\n",
+					  dev_name(&dev->dev));
+			acpihp_slot_set_flag(slot,
+					     ACPIHP_SLOT_FLAG_IRREMOVABLE);
+		} else
+			rv = AE_OK;
+	} else {
+		ACPIHP_SLOT_DEBUG(slot, "fails to query device info of %s.\n",
+				  dev_name(&dev->dev));
+		acpihp_slot_set_flag(slot, ACPIHP_SLOT_FLAG_IRREMOVABLE);
+	}
+
+	kfree(info);
+
+	return rv;
+}
+
+/*
+ * Enumerator all devices connecting to a slot and add them onto slot's
+ * device lists.
+ */
+int acpihp_drv_enumerate_devices(struct acpihp_slot *slot)
+{
+	return acpihp_walk_devices(slot->handle, acpihp_drv_enum_device, slot);
+}
+
+static void acpihp_drv_remove_devices(struct acpihp_slot *slot)
+{
+	enum acpihp_dev_type type;
+
+	for (type = ACPIHP_DEV_TYPE_UNKNOWN; type < ACPIHP_DEV_TYPE_MAX; type++)
+		acpihp_remove_device_list(&slot->dev_lists[type]);
+}
+
+/* Callback function for ACPI system event notification. */
+static void acpihp_drv_event_handler(acpi_handle handle, u32 event,
+				     void *context)
+{
+	/* TODO: handle ACPI hotplug events */
+}
+
+static acpi_status acpihp_drv_install_handler(struct acpihp_slot *slot)
+{
+	acpi_status status;
+
+	status = acpi_install_notify_handler(slot->handle, ACPI_SYSTEM_NOTIFY,
+					     acpihp_drv_event_handler, slot);
+	ACPIHP_SLOT_DEBUG(slot, "%s to install event handler.\n",
+			  ACPI_SUCCESS(status) ? "succeeds" : "fails");
+
+	return status;
+}
+
+static void acpihp_drv_uninstall_handler(struct acpihp_slot *slot)
+{
+	acpi_status status;
+
+	status = acpi_remove_notify_handler(slot->handle, ACPI_SYSTEM_NOTIFY,
+					    acpihp_drv_event_handler);
+	ACPIHP_SLOT_DEBUG(slot, "%s to uninstall event handler.\n",
+			  ACPI_SUCCESS(status) ? "succeeds" : "fails");
+}
+
+static int acpihp_drv_slot_add(struct device *dev, struct class_interface *intf)
+{
+	struct acpihp_slot_drv *drv_data;
+	struct acpihp_slot *slot = container_of(dev, struct acpihp_slot, dev);
+
+	/*
+	 * Try to hold a reference to the slot_ops structure to prevent
+	 * the platform specific enumerator driver from unloading.
+	 */
+	if (!slot->slot_ops || !try_module_get(slot->slot_ops->owner)) {
+		ACPIHP_SLOT_DEBUG(slot,
+				  "fails to get reference to slot_ops.\n");
+		return -EINVAL;
+	}
+
+	/* install ACPI event notification handler for slot */
+	if (ACPI_FAILURE(acpihp_drv_install_handler(slot))) {
+		ACPIHP_SLOT_DEBUG(slot, "fails to install event handler.\n");
+		module_put(slot->slot_ops->owner);
+		return -EBUSY;
+	}
+
+	/* Enumerate all devices if slot is already powered. */
+	if (!acpihp_slot_powered(slot))
+		ACPIHP_SLOT_DEBUG(slot, "is powered off.\n");
+	else if (acpihp_drv_enumerate_devices(slot))
+		acpihp_slot_set_flag(slot, ACPIHP_SLOT_FLAG_IRREMOVABLE);
+
+	acpihp_drv_update_slot_state(slot);
+
+	drv_data = kzalloc(sizeof(*drv_data), GFP_KERNEL);
+	if (drv_data) {
+		drv_data->event_flag = ACPIHP_DRV_EVENT_FROM_FW;
+		mutex_init(&drv_data->op_mutex);
+		INIT_LIST_HEAD(&drv_data->depend_list);
+	}
+	if (drv_data == NULL ||
+	    acpihp_slot_attach_drv_data(slot, intf, (void *)drv_data)) {
+		ACPIHP_SLOT_DEBUG(slot, "fails to attach driver data.\n");
+		acpihp_drv_remove_devices(slot);
+		module_put(slot->slot_ops->owner);
+		kfree(drv_data);
+		return -ENOMEM;
+	}
+
+	return 0;
+}
+
+static void acpihp_drv_intf_remove(struct device *dev,
+				  struct class_interface *intf)
+{
+	struct acpihp_slot_drv *drv_data = NULL;
+	struct acpihp_slot *slot =
+			container_of(dev, struct acpihp_slot, dev);
+
+	acpihp_drv_uninstall_handler(slot);
+	acpihp_drv_remove_devices(slot);
+	acpihp_slot_detach_drv_data(slot, intf, (void **)&drv_data);
+	if (drv_data != NULL)
+		kfree(drv_data);
+
+	module_put(slot->slot_ops->owner);
+}
+
+/*
+ * register a class driver onto the acpihp_slot_class to manage all system
+ * device hotplug slots.
+ */
+static struct class_interface acpihp_drv_interface = {
+	.class		= &acpihp_slot_class,
+	.add_dev	= acpihp_drv_slot_add,
+	.remove_dev	= acpihp_drv_intf_remove,
+};
+
+static int __init acpihp_drv_init(void)
+{
+	int retval;
+
+	retval = acpihp_register_class();
+	if (retval) {
+		ACPIHP_DEBUG("fails to register ACPI hotplug slot class.\n");
+		return retval;
+	}
+
+	retval = class_interface_register(&acpihp_drv_interface);
+	if (retval) {
+		ACPIHP_DEBUG("fails to register ACPI hotplug slot driver.\n");
+		acpihp_unregister_class();
+	}
+
+	return retval;
+}
+
+static void __exit acpihp_drv_exit(void)
+{
+	class_interface_unregister(&acpihp_drv_interface);
+	acpihp_unregister_class();
+}
+
+module_init(acpihp_drv_init);
+module_exit(acpihp_drv_exit);
+
+MODULE_LICENSE("GPL v2");
+MODULE_AUTHOR("Jiang Liu <jiang.liu@huawei.com>");
+MODULE_AUTHOR("Hanjun Guo <guohanjun@huawei.com>");
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 12/28] ACPIHP: analyse dependencies among ACPI hotplug slots
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (10 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 11/28] ACPIHP: implement skeleton of ACPI system device hotplug driver Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 13/28] ACPIHP: provide interface to cancel inprogress hotplug operations Jiang Liu
                   ` (16 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

Due to hardware constraints, an ACPI hotplug slot may have dependencies
on other ACPI hotplug slots. For example, if a hotpluggable memory board
is connected to a hotpluggble physical processor, the physical processor
must be powered on before powering the memory board on.

According to physical and device tree topology constraints, we need to
consider following dependency relationships:
1) The parent slot must be powered on before powering a child slot on.
2) All child slots must be powered off before powering a parent slot off.
3) All devices in a slot's _EDL list must be powered off before powering
   a slot off.
4) The parent ACPI device topology must be created before creating ACPI
   devices for devices connecting to a child slot
5) All ACPI devices connecting to child slots must be destroyed before
   destroying ACPI device topology for a parent slot.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
Signed-off-by: Hanjun Guo <guohanjun@huawei.com>
---
 drivers/acpi/hotplug/Makefile     |    1 +
 drivers/acpi/hotplug/acpihp_drv.h |   14 +++
 drivers/acpi/hotplug/dependency.c |  249 +++++++++++++++++++++++++++++++++++++
 3 files changed, 264 insertions(+)
 create mode 100644 drivers/acpi/hotplug/dependency.c

diff --git a/drivers/acpi/hotplug/Makefile b/drivers/acpi/hotplug/Makefile
index 6257047..bfb677f 100644
--- a/drivers/acpi/hotplug/Makefile
+++ b/drivers/acpi/hotplug/Makefile
@@ -12,3 +12,4 @@ acpihp_slot-$(CONFIG_ACPI_HOTPLUG_SLOT_FAKE)	+= slot_fake.o
 
 obj-$(CONFIG_ACPI_HOTPLUG_DRIVER)		+= acpihp_drv.o
 acpihp_drv-y					= drv_main.o
+acpihp_drv-y					+= dependency.o
diff --git a/drivers/acpi/hotplug/acpihp_drv.h b/drivers/acpi/hotplug/acpihp_drv.h
index 9b1c1c6..43a44ce 100644
--- a/drivers/acpi/hotplug/acpihp_drv.h
+++ b/drivers/acpi/hotplug/acpihp_drv.h
@@ -53,10 +53,24 @@ struct acpihp_slot_drv {
 	struct acpihp_cancel_context	cancel_ctx;
 };
 
+struct acpihp_slot_dependency {
+	struct list_head		node;
+	struct acpihp_slot		*slot;
+	u32				opcodes;
+};
+
 void acpihp_drv_get_data(struct acpihp_slot *slot,
 			 struct acpihp_slot_drv **data);
 int acpihp_drv_enumerate_devices(struct acpihp_slot *slot);
 void acpihp_drv_update_slot_state(struct acpihp_slot *slot);
 int acpihp_drv_update_slot_status(struct acpihp_slot *slot);
 
+int acpihp_drv_add_slot_to_dependency_list(struct acpihp_slot *slot,
+					   struct list_head *slot_list);
+void acpihp_drv_destroy_dependency_list(struct list_head *slot_list);
+int acpihp_drv_filter_dependency_list(struct list_head *old_head,
+		struct list_head *new_head, u32 opcode);
+int acpihp_drv_generate_dependency_list(struct acpihp_slot *slot,
+		struct list_head *slot_list, enum acpihp_drv_cmd cmd);
+
 #endif	/* __ACPIHP_DRV_H__ */
diff --git a/drivers/acpi/hotplug/dependency.c b/drivers/acpi/hotplug/dependency.c
new file mode 100644
index 0000000..659f4e4
--- /dev/null
+++ b/drivers/acpi/hotplug/dependency.c
@@ -0,0 +1,249 @@
+/*
+ * Copyright (C) 2012 Huawei Tech. Co., Ltd.
+ * Copyright (C) 2012 Jiang Liu <jiang.liu@huawei.com>
+ * Copyright (C) 2012 Hanjun Guo <guohanjun@huawei.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/kernel.h>
+#include <linux/types.h>
+#include <linux/list.h>
+#include <linux/mutex.h>
+#include <linux/acpi.h>
+#include <acpi/acpi_hotplug.h>
+#include "acpihp_drv.h"
+
+#define	ACPI_METHOD_NAME__EDL	"_EDL"
+
+/*
+ * Insert a slot into the dependency list in FILO order.
+ * Caller needs to protect from concurrent accesses to the dependency list.
+ */
+int acpihp_drv_add_slot_to_dependency_list(struct acpihp_slot *slot,
+					   struct list_head *dep_list)
+{
+	struct acpihp_slot_dependency *dep;
+
+	/*
+	 * A dependent slot may be encountered when both analyzing the array
+	 * returned by _EDL method and walking ACPI namespace topology.
+	 * Should we move the slot to the list head? May need more work
+	 * here on platforms with complex topology.
+	 */
+	list_for_each_entry(dep, dep_list, node)
+		if (dep->slot == slot)
+			return 0;
+
+	dep = kzalloc(sizeof(*dep), GFP_KERNEL);
+	if (!dep) {
+		ACPIHP_SLOT_DEBUG(slot, "fails to allocate memory.\n");
+		return -ENOMEM;
+	}
+
+	dep->slot = slot;
+	list_add(&dep->node, dep_list);
+
+	return 0;
+}
+
+static int acpihp_drv_get_online_dependency(struct acpihp_slot *slot,
+					    struct list_head *dep_list)
+{
+	int ret = 0;
+	struct acpihp_slot *temp;
+
+	/*
+	 * When enabling a hotplug slot, all its ancestors must be enabled
+	 * first.
+	 */
+	for (temp = slot; temp && ret == 0; temp = temp->parent)
+		ret = acpihp_drv_add_slot_to_dependency_list(temp, dep_list);
+
+	return ret;
+}
+
+/*
+ * Analyze dependency relationships by evaulating ACPI _EDL method
+ * when disabling a hotplug slot.
+ */
+static int acpihp_drv_for_each_edl(struct acpihp_slot *slot, void *argp,
+	int(*cb)(struct device *dev, void *argp))
+{
+	int i;
+	acpi_status rc;
+	struct acpi_buffer buf;
+	union acpi_object *obj, *elem;
+	struct acpihp_slot *tmp;
+
+	buf.length = ACPI_ALLOCATE_BUFFER;
+	rc = acpi_evaluate_object_typed(slot->handle, ACPI_METHOD_NAME__EDL,
+					NULL, &buf, ACPI_TYPE_PACKAGE);
+	if (rc == AE_NOT_FOUND) {
+		/* ACPI _EDL method is optional. */
+		return 0;
+	} else if (ACPI_FAILURE(rc)) {
+		ACPIHP_SLOT_DEBUG(slot, "fails to evaluate _EDL.\n");
+		return -EINVAL;
+	}
+	obj = buf.pointer;
+
+	/* validate the returned package object. */
+	for (i = 0, elem = obj->package.elements;
+	     i < obj->package.count; i++, elem++)
+		if (elem->type != ACPI_TYPE_LOCAL_REFERENCE ||
+		    elem->reference.actual_type != ACPI_TYPE_DEVICE ||
+		    elem->reference.handle == NULL) {
+			ACPIHP_SLOT_DEBUG(slot,
+					  "invalid return from _EDL method.\n");
+			rc = AE_ERROR;
+			goto out;
+		}
+
+	/*
+	 * The dependency list will be handled in FILO order, so walk the array
+	 * in reverse order to keep the same order as returned by _EDL.
+	 */
+	for (i = 0, elem--; i < obj->package.count && ACPI_SUCCESS(rc);
+	     i++, elem--)
+		if (acpihp_get_slot(elem->reference.handle, &tmp)) {
+			rc = (*cb)(&tmp->dev, argp);
+			if (rc == AE_CTRL_DEPTH || rc == AE_CTRL_TERMINATE)
+				rc = AE_OK;
+		/*
+		 * ACPI _EDL method may return PCI slots for a hotpluggable
+		 * PCI host bridge, skip such cases. Only bail out if it's
+		 * an ACPI hotplug slot for system devices.
+		 */
+		} else if (acpihp_is_slot(elem->reference.handle)) {
+			ACPIHP_SLOT_WARN(slot,
+					 "fails to get device for slot.\n");
+			rc = AE_ERROR;
+		}
+
+out:
+	ACPI_FREE(buf.pointer);
+
+	return ACPI_SUCCESS(rc) ? 0 : -EINVAL;
+}
+
+static int acpihp_drv_add_offline_dependency(struct device *dev, void *argp)
+{
+	int ret;
+	struct acpihp_slot *slot;
+	struct acpihp_slot_dependency *dep;
+	struct list_head *list = argp;
+
+	slot = container_of(dev, struct acpihp_slot, dev);
+	ret = acpihp_drv_add_slot_to_dependency_list(slot, list);
+	list_for_each_entry_reverse(dep, list, node) {
+		if (!ret)
+			ret = device_for_each_child(&slot->dev, argp,
+					&acpihp_drv_add_offline_dependency);
+		if (!ret)
+			ret = acpihp_drv_for_each_edl(slot, argp,
+				&acpihp_drv_add_offline_dependency);
+		if (ret)
+			break;
+	}
+
+	return ret;
+}
+
+static int acpihp_drv_get_offline_dependency(struct acpihp_slot *slot,
+					     struct list_head *dep_list)
+{
+	return acpihp_drv_add_offline_dependency(&slot->dev, dep_list);
+}
+
+/*
+ * Genereate dependency list for a given slot according to command.
+ * Caller needs to clean up the returned list if error happens.
+ */
+int acpihp_drv_generate_dependency_list(struct acpihp_slot *slot,
+		struct list_head *slot_list, enum acpihp_drv_cmd cmd)
+{
+	int retval;
+
+	switch (cmd) {
+	case ACPIHP_DRV_CMD_POWERON:
+	/* fall through */
+	case ACPIHP_DRV_CMD_CONNECT:
+	/* fall through */
+	case ACPIHP_DRV_CMD_CONFIGURE:
+		retval = acpihp_drv_get_online_dependency(slot, slot_list);
+		break;
+
+	case ACPIHP_DRV_CMD_POWEROFF:
+	/* fall through */
+	case ACPIHP_DRV_CMD_DISCONNECT:
+	/* fall through */
+	case ACPIHP_DRV_CMD_UNCONFIGURE:
+		retval = acpihp_drv_get_offline_dependency(slot, slot_list);
+		break;
+
+	default:
+		retval = -EINVAL;
+		break;
+	}
+
+	return retval;
+}
+
+/*
+ * Generate a new list with slots from the old list which need to
+ * execute a specific operation.
+ */
+int acpihp_drv_filter_dependency_list(struct list_head *old_head,
+		struct list_head *new_head, u32 opcode)
+{
+	struct acpihp_slot_dependency *old_dep, *new_dep;
+
+	/* Initialize new list to empty */
+	INIT_LIST_HEAD(new_head);
+
+	list_for_each_entry(old_dep, old_head, node) {
+		/* Skip if the specified operation is not needed. */
+		if (!(old_dep->opcodes & opcode))
+			continue;
+
+		new_dep = kzalloc(sizeof(*new_dep), GFP_KERNEL);
+		if (!new_dep) {
+			ACPIHP_DEBUG("fails to filter depend list.\n");
+			acpihp_drv_destroy_dependency_list(new_head);
+			return -ENOMEM;
+		}
+
+		new_dep->slot = old_dep->slot;
+		new_dep->opcodes = old_dep->opcodes;
+		list_add_tail(&new_dep->node, new_head);
+	}
+
+	return 0;
+}
+
+void acpihp_drv_destroy_dependency_list(struct list_head *slot_list)
+{
+	struct acpihp_slot_dependency *dep, *temp;
+
+	list_for_each_entry_safe(dep, temp, slot_list, node) {
+		list_del(&dep->node);
+		kfree(dep);
+	}
+}
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 13/28] ACPIHP: provide interface to cancel inprogress hotplug operations
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (11 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 12/28] ACPIHP: analyse dependencies among ACPI hotplug slots Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 14/28] ACPIHP: configure/unconfigure system devices connecting to a hotplug slot Jiang Liu
                   ` (15 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

Some hotplug operations, such as hot-removal of memory device, may take
very long or even infinite time. One possible solution is to time out
and retry, but it's sub-optimal.

This patch implements interfaces to cancel inprogress ACPI system device
hotplug operations, so user could cancel a long-standing hotplug request
on demand.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
Signed-off-by: Hanjun Guo <guohanjun@huawei.com>
---
 drivers/acpi/hotplug/Makefile     |    1 +
 drivers/acpi/hotplug/acpihp_drv.h |   16 ++++
 drivers/acpi/hotplug/cancel.c     |  174 +++++++++++++++++++++++++++++++++++++
 3 files changed, 191 insertions(+)
 create mode 100644 drivers/acpi/hotplug/cancel.c

diff --git a/drivers/acpi/hotplug/Makefile b/drivers/acpi/hotplug/Makefile
index bfb677f..f72f2c3 100644
--- a/drivers/acpi/hotplug/Makefile
+++ b/drivers/acpi/hotplug/Makefile
@@ -13,3 +13,4 @@ acpihp_slot-$(CONFIG_ACPI_HOTPLUG_SLOT_FAKE)	+= slot_fake.o
 obj-$(CONFIG_ACPI_HOTPLUG_DRIVER)		+= acpihp_drv.o
 acpihp_drv-y					= drv_main.o
 acpihp_drv-y					+= dependency.o
+acpihp_drv-y					+= cancel.o
diff --git a/drivers/acpi/hotplug/acpihp_drv.h b/drivers/acpi/hotplug/acpihp_drv.h
index 43a44ce..1661e52 100644
--- a/drivers/acpi/hotplug/acpihp_drv.h
+++ b/drivers/acpi/hotplug/acpihp_drv.h
@@ -44,6 +44,15 @@ enum acpihp_dev_event {
 	ACPIHP_DRV_EVENT_FROM_OS
 };
 
+enum acpihp_drv_cancel_state {
+	ACPIHP_DRV_CANCEL_INIT = 0,
+	ACPIHP_DRV_CANCEL_STARTED,
+	ACPIHP_DRV_CANCEL_OK,
+	ACPIHP_DRV_CANCEL_FAILED,
+	ACPIHP_DRV_CANCEL_MISSED,
+	ACPIHP_DRV_CANCEL_FINISHED
+};
+
 struct acpihp_slot_drv {
 	enum acpihp_dev_event	event_flag;
 	struct mutex		op_mutex; /* Prevent concurrent hotplugs. */
@@ -73,4 +82,11 @@ int acpihp_drv_filter_dependency_list(struct list_head *old_head,
 int acpihp_drv_generate_dependency_list(struct acpihp_slot *slot,
 		struct list_head *slot_list, enum acpihp_drv_cmd cmd);
 
+void acpihp_drv_cancel_init(struct list_head *list);
+void acpihp_drv_cancel_notify(struct acpihp_slot *slot,
+			      enum acpihp_drv_cancel_state state);
+void acpihp_drv_cancel_fini(struct list_head *list);
+int acpihp_drv_cancel_start(struct list_head *list);
+int acpihp_drv_cancel_wait(struct list_head *list);
+
 #endif	/* __ACPIHP_DRV_H__ */
diff --git a/drivers/acpi/hotplug/cancel.c b/drivers/acpi/hotplug/cancel.c
new file mode 100644
index 0000000..44e9b4a
--- /dev/null
+++ b/drivers/acpi/hotplug/cancel.c
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2012 Huawei Tech. Co., Ltd.
+ * Copyright (C) 2012 Jiang Liu <jiang.liu@huawei.com>
+ * Copyright (C) 2012 Hanjun Guo <guohanjun@huawei.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/wait.h>
+#include <acpi/acpi_hotplug.h>
+#include "acpihp_drv.h"
+
+/*
+ * Implements interfaces to cancel inprogress hotplug operations.
+ * Currently only CONFIGURE and RELEASE operation stages support cancellation.
+ * Caller must serialize calls to following functions by holding the
+ * state_machine_mutex lock:
+ *	acpihp_drv_cancel_init()
+ *	acpihp_drv_cancel_fini()
+ *	acpihp_drv_cancel_start()
+ */
+static DECLARE_WAIT_QUEUE_HEAD(acpihp_drv_cancel_queue);
+
+static int acpihp_drv_check_cancel(struct acpihp_cancel_context *ctx)
+{
+	struct acpihp_slot_drv *drv_data;
+
+	BUG_ON(ctx == NULL);
+	drv_data = container_of(ctx, struct acpihp_slot_drv, cancel_ctx);
+
+	return atomic_read(&drv_data->cancel_state) != ACPIHP_DRV_CANCEL_INIT;
+}
+
+void acpihp_drv_cancel_init(struct list_head *list)
+{
+	struct acpihp_slot_drv *drv_data;
+	struct acpihp_slot_dependency *dep;
+
+	/* Wait for all cancellation threads to exit */
+	list_for_each_entry(dep, list, node) {
+		acpihp_drv_get_data(dep->slot, &drv_data);
+		drv_data->cancel_ctx.check_cancel = acpihp_drv_check_cancel;
+		atomic_set(&drv_data->cancel_state, ACPIHP_DRV_CANCEL_INIT);
+		atomic_set(&drv_data->cancel_users, 0);
+	}
+}
+
+void acpihp_drv_cancel_notify(struct acpihp_slot *slot,
+			      enum acpihp_drv_cancel_state state)
+{
+	int old;
+	struct acpihp_slot_drv *drv_data;
+
+	acpihp_drv_get_data(slot, &drv_data);
+	old = atomic_cmpxchg(&drv_data->cancel_state, ACPIHP_DRV_CANCEL_INIT,
+			     ACPIHP_DRV_CANCEL_FINISHED);
+	if (old != ACPIHP_DRV_CANCEL_INIT) {
+		atomic_set(&drv_data->cancel_state, state);
+		wake_up_all(&acpihp_drv_cancel_queue);
+	}
+}
+
+/*
+ * Wait for all cancellation threads to give up their reference count.
+ *
+ * Caller must provide mechanism to avoid currently running
+ * acpihp_drv_cancel_start() and acpihp_drv_cancel_fini()
+ * on the same list.
+ */
+void acpihp_drv_cancel_fini(struct list_head *list)
+{
+	int state;
+	struct acpihp_slot_drv *drv_data;
+	struct acpihp_slot_dependency *dep;
+
+	list_for_each_entry(dep, list, node) {
+		acpihp_drv_get_data(dep->slot, &drv_data);
+
+		/*
+		 * Wake up all cancellation threads if they are still
+		 * STARTED state.
+		 */
+		state = atomic_cmpxchg(&drv_data->cancel_state,
+				       ACPIHP_DRV_CANCEL_STARTED,
+				       ACPIHP_DRV_CANCEL_MISSED);
+		if (state == ACPIHP_DRV_CANCEL_STARTED)
+			wake_up_all(&acpihp_drv_cancel_queue);
+
+		/* Wait for all cancellation threads to exit */
+		wait_event(acpihp_drv_cancel_queue,
+			   !atomic_read(&drv_data->cancel_users));
+	}
+}
+
+/*
+ * Start cancellation on a list of hotplug slots.
+ *
+ * Caller must provide mechanism to avoid currently running
+ * acpihp_drv_cancel_start() and acpihp_drv_cancel_fini()
+ * on the same list.
+ */
+int acpihp_drv_cancel_start(struct list_head *list)
+{
+	struct acpihp_slot_drv *drv_data;
+	struct acpihp_slot_dependency *dep;
+
+	if (list_empty(list)) {
+		ACPIHP_DEBUG("dependency list is empty.\n");
+		return -ENODEV;
+	}
+
+	/* Start cancellation on all slots. */
+	list_for_each_entry(dep, list, node) {
+		acpihp_drv_get_data(dep->slot, &drv_data);
+		atomic_inc(&drv_data->cancel_users);
+		atomic_cmpxchg(&drv_data->cancel_state,
+			       ACPIHP_DRV_CANCEL_INIT,
+			       ACPIHP_DRV_CANCEL_STARTED);
+	}
+
+	return 0;
+}
+
+/*
+ * Wait for all slots on the list to reach a stable state and then check
+ * cancellation result.
+ */
+int acpihp_drv_cancel_wait(struct list_head *list)
+{
+	int state, result = 0;
+	struct acpihp_slot_drv *drv_data;
+	struct acpihp_slot_dependency *dep;
+
+	list_for_each_entry(dep, list, node) {
+		acpihp_drv_get_data(dep->slot, &drv_data);
+		wait_event(acpihp_drv_cancel_queue,
+			   atomic_read(&drv_data->cancel_state)
+				!= ACPIHP_DRV_CANCEL_STARTED);
+
+		state = atomic_read(&drv_data->cancel_state);
+		if (state == ACPIHP_DRV_CANCEL_FAILED) {
+			ACPIHP_SLOT_DEBUG(dep->slot,
+					  "fails to cancel operation.\n");
+			result = result ? : -EBUSY;
+		} else if (state == ACPIHP_DRV_CANCEL_MISSED) {
+			ACPIHP_SLOT_DEBUG(dep->slot,
+					  "misses to cancel operation.\n");
+			result = result ? : -EBUSY;
+		}
+
+		atomic_set(&drv_data->cancel_state,
+			   ACPIHP_DRV_CANCEL_FINISHED);
+		atomic_dec(&drv_data->cancel_users);
+		wake_up_all(&acpihp_drv_cancel_queue);
+	}
+
+	return result;
+}
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 14/28] ACPIHP: configure/unconfigure system devices connecting to a hotplug slot
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (12 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 13/28] ACPIHP: provide interface to cancel inprogress hotplug operations Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 15/28] ACPIHP: implement the state machine for ACPI hotplug slots Jiang Liu
                   ` (14 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

This patch implements functions to configure/unconfigure system devices
connecting to a hotplug slot.

To support better error recover and cancellation, configuration operations are
splitted into three steps and unconfiguration operations are splitted into six
steps as below:
CONFIGURE
 1) pre_configure(): allocate required resources
 2) configure(): add devices into system
 3) pos_configure(): rollback if cancelled or failed to add devices
UNCONFIGURE
 1) pre_release(): optional
 2) release(): reclaim devices from system
 3) post_release(): rollback if cancelled or failed to reclaim devices
 4) pre_unconfigure(): optional
 5) unconfigure(): remove devices from system
 6) post_unconfigure(): free resources used by devices

And all devices are unconfigured in reverse order to solve failures caused
by dependencies.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
Signed-off-by: Hanjun Guo <guohanjun@huawei.com>
Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
---
 drivers/acpi/hotplug/Makefile     |    1 +
 drivers/acpi/hotplug/acpihp_drv.h |    3 +
 drivers/acpi/hotplug/configure.c  |  340 +++++++++++++++++++++++++++++++++++++
 3 files changed, 344 insertions(+)
 create mode 100644 drivers/acpi/hotplug/configure.c

diff --git a/drivers/acpi/hotplug/Makefile b/drivers/acpi/hotplug/Makefile
index f72f2c3..6cb6aa1 100644
--- a/drivers/acpi/hotplug/Makefile
+++ b/drivers/acpi/hotplug/Makefile
@@ -14,3 +14,4 @@ obj-$(CONFIG_ACPI_HOTPLUG_DRIVER)		+= acpihp_drv.o
 acpihp_drv-y					= drv_main.o
 acpihp_drv-y					+= dependency.o
 acpihp_drv-y					+= cancel.o
+acpihp_drv-y					+= configure.o
diff --git a/drivers/acpi/hotplug/acpihp_drv.h b/drivers/acpi/hotplug/acpihp_drv.h
index 1661e52..8fa37be 100644
--- a/drivers/acpi/hotplug/acpihp_drv.h
+++ b/drivers/acpi/hotplug/acpihp_drv.h
@@ -89,4 +89,7 @@ void acpihp_drv_cancel_fini(struct list_head *list);
 int acpihp_drv_cancel_start(struct list_head *list);
 int acpihp_drv_cancel_wait(struct list_head *list);
 
+int acpihp_drv_configure(struct list_head *list);
+int acpihp_drv_unconfigure(struct list_head *list);
+
 #endif	/* __ACPIHP_DRV_H__ */
diff --git a/drivers/acpi/hotplug/configure.c b/drivers/acpi/hotplug/configure.c
new file mode 100644
index 0000000..346b976
--- /dev/null
+++ b/drivers/acpi/hotplug/configure.c
@@ -0,0 +1,340 @@
+/*
+ * Copyright (C) 2012 Huawei Tech. Co., Ltd.
+ * Copyright (C) 2012 Jiang Liu <jiang.liu@huawei.com>
+ * Copyright (C) 2012 Hanjun Guo <guohanjun@huawei.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/acpi.h>
+#include <linux/device.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_hotplug.h>
+#include "acpihp_drv.h"
+
+enum config_op_code {
+	DRV_OP_PRE_CONFIGURE = 0xAC91,
+	DRV_OP_CONFIGURE,
+	DRV_OP_POST_CONFIGURE,
+	DRV_OP_PRE_RELEASE,
+	DRV_OP_RELEASE,
+	DRV_OP_POST_RELEASE,
+	DRV_OP_PRE_UNCONFIGURE,
+	DRV_OP_UNCONFIGURE,
+	DRV_OP_POST_UNCONFIGURE
+};
+
+/* All devices will be configured in following order. */
+static enum acpihp_dev_type acpihp_drv_dev_types[] = {
+	ACPIHP_DEV_TYPE_CONTAINER,
+	ACPIHP_DEV_TYPE_MEM,
+	ACPIHP_DEV_TYPE_CPU,
+	ACPIHP_DEV_TYPE_IOAPIC,
+	ACPIHP_DEV_TYPE_HOST_BRIDGE,
+};
+
+/* All devices will be unconfigured in following order. */
+static enum acpihp_dev_type acpihp_drv_dev_types_reverse[] = {
+	ACPIHP_DEV_TYPE_HOST_BRIDGE,
+	ACPIHP_DEV_TYPE_IOAPIC,
+	ACPIHP_DEV_TYPE_CPU,
+	ACPIHP_DEV_TYPE_MEM,
+	ACPIHP_DEV_TYPE_CONTAINER,
+};
+
+static void acpihp_drv_update_dev_state(struct acpihp_dev_node *dev,
+					enum acpihp_dev_state state)
+{
+	BUG_ON(state <= DEVICE_STATE_UNKOWN || state >= DEVICE_STATE_MAX);
+	mutex_lock(&dev->lock);
+	dev->state = state;
+	mutex_unlock(&dev->lock);
+}
+
+static int acpihp_drv_invoke_method(enum config_op_code  opcode,
+				    struct acpihp_slot *slot,
+				    struct acpi_device *dev, int post)
+{
+	struct acpihp_slot_drv *drv_data;
+
+	acpihp_drv_get_data(slot, &drv_data);
+
+	switch (opcode) {
+	case DRV_OP_PRE_CONFIGURE:
+		if (drv_data->cancel_ctx.check_cancel(&drv_data->cancel_ctx))
+			return -ECANCELED;
+		return acpihp_dev_pre_configure(dev, &drv_data->cancel_ctx);
+	case DRV_OP_CONFIGURE:
+		if (drv_data->cancel_ctx.check_cancel(&drv_data->cancel_ctx))
+			return -ECANCELED;
+		return acpihp_dev_configure(dev, &drv_data->cancel_ctx);
+	case DRV_OP_POST_CONFIGURE:
+		return acpihp_dev_post_configure(dev, post);
+	case DRV_OP_PRE_RELEASE:
+		if (drv_data->cancel_ctx.check_cancel(&drv_data->cancel_ctx))
+			return -ECANCELED;
+		return acpihp_dev_pre_release(dev, &drv_data->cancel_ctx);
+	case DRV_OP_RELEASE:
+		if (drv_data->cancel_ctx.check_cancel(&drv_data->cancel_ctx))
+			return -ECANCELED;
+		return acpihp_dev_release(dev, &drv_data->cancel_ctx);
+	case DRV_OP_POST_RELEASE:
+		return acpihp_dev_post_release(dev, post);
+	case DRV_OP_PRE_UNCONFIGURE:
+		return acpihp_dev_pre_unconfigure(dev);
+	case DRV_OP_UNCONFIGURE:
+		return acpihp_dev_unconfigure(dev);
+	case DRV_OP_POST_UNCONFIGURE:
+		return acpihp_dev_post_unconfigure(dev);
+	default:
+		BUG_ON(opcode);
+		return -ENOSYS;
+	}
+}
+
+static int acpihp_drv_call_method(enum config_op_code opcode,
+				  struct acpihp_slot *slot,
+				  enum acpihp_dev_type type,
+				  enum acpihp_dev_state state)
+{
+	int result = 0;
+	struct klist_iter iter;
+	struct klist_node *ip;
+	struct acpihp_dev_node *np;
+	struct acpi_device *acpi_dev;
+
+	klist_iter_init(&slot->dev_lists[type], &iter);
+	while ((ip = klist_next(&iter)) != NULL) {
+		np = container_of(ip, struct acpihp_dev_node, node);
+		acpi_dev = container_of(np->dev, struct acpi_device, dev);
+		result = acpihp_drv_invoke_method(opcode, slot, acpi_dev, 0);
+		if (result)
+			break;
+		acpihp_drv_update_dev_state(np, state);
+	}
+	klist_iter_exit(&iter);
+
+	return result;
+}
+
+static int acpihp_drv_call_method_post(enum config_op_code opcode,
+				       struct acpihp_slot *slot,
+				       enum acpihp_dev_type type,
+				       enum acpihp_dev_state state,
+				       enum acpihp_dev_post_cmd post)
+{
+	int retval = 0;
+	int result;
+	struct klist_iter iter;
+	struct klist_node *ip;
+	struct acpihp_dev_node *np;
+	struct acpi_device *acpi_dev;
+
+	klist_iter_init(&slot->dev_lists[type], &iter);
+	while ((ip = klist_next(&iter)) != NULL) {
+		np = container_of(ip, struct acpihp_dev_node, node);
+		acpi_dev = container_of(np->dev, struct acpi_device, dev);
+		if (np->state == state && post == ACPIHP_DEV_POST_CMD_ROLLBACK)
+			continue;
+
+		result = acpihp_drv_invoke_method(opcode, slot, acpi_dev, post);
+		if (result)
+			retval = -EIO;
+		else if (post == ACPIHP_DEV_POST_CMD_ROLLBACK)
+			acpihp_drv_update_dev_state(np, state);
+	}
+	klist_iter_exit(&iter);
+
+	return retval;
+}
+
+static int acpihp_drv_walk_devs(struct list_head *slot_list,
+				enum config_op_code opcode,
+				enum acpihp_dev_state state,
+				bool reverse)
+{
+	int i, retval = 0;
+	enum acpihp_dev_type *tp;
+	struct acpihp_slot_dependency *dep;
+	int count = ARRAY_SIZE(acpihp_drv_dev_types);
+
+	tp = reverse ? acpihp_drv_dev_types_reverse : acpihp_drv_dev_types;
+	for (i = 0; i < count; i++)
+		list_for_each_entry(dep, slot_list, node) {
+			retval = acpihp_drv_call_method(opcode, dep->slot,
+							tp[i], state);
+			if (retval)
+				return retval;
+		}
+
+	return 0;
+}
+
+static void acpihp_drv_walk_devs_post(struct list_head *slot_list,
+				      enum config_op_code opcode,
+				      enum acpihp_dev_state state,
+				      enum acpihp_dev_post_cmd post,
+				      bool reverse)
+{
+	int i;
+	enum acpihp_dev_type *tp;
+	struct acpihp_slot_dependency *dep;
+	int count = ARRAY_SIZE(acpihp_drv_dev_types);
+
+	tp = reverse ? acpihp_drv_dev_types_reverse : acpihp_drv_dev_types;
+	for (i = 0; i < count; i++)
+		list_for_each_entry(dep, slot_list, node)
+			if (acpihp_drv_call_method_post(opcode, dep->slot,
+							tp[i], state, post))
+				ACPIHP_SLOT_WARN(dep->slot,
+					"fails to commit or rollback.\n");
+}
+
+static void acpihp_drv_sync_cancel(struct list_head *list, int result)
+{
+	int cancel;
+	struct acpihp_slot_dependency *dep;
+
+	if (!result)
+		cancel = ACPIHP_DRV_CANCEL_MISSED;
+	else if (result == -ECANCELED)
+		cancel = ACPIHP_DRV_CANCEL_OK;
+	else
+		cancel = ACPIHP_DRV_CANCEL_FAILED;
+	list_for_each_entry(dep, list, node) {
+		acpihp_drv_cancel_notify(dep->slot, cancel);
+		acpihp_drv_update_slot_state(dep->slot);
+	}
+}
+
+/*
+ * To support better error recover and cancellation, configure operations
+ * are splitted into three steps:
+ * 1) pre_configure(): allocate required resources
+ * 2) configure(): add devices into system
+ * 3) post_configure(): rollback if cancelled or failed to add devices
+ */
+int acpihp_drv_configure(struct list_head *list)
+{
+	int result;
+	struct list_head head;
+	struct acpihp_slot_dependency *dep;
+	enum acpihp_dev_post_cmd post = ACPIHP_DEV_POST_CMD_COMMIT;
+
+	result = acpihp_drv_filter_dependency_list(list, &head,
+						   ACPIHP_DRV_CMD_CONFIGURE);
+	if (result) {
+		ACPIHP_DEBUG("fails to filter dependency list.\n");
+		return -ENOMEM;
+	}
+
+	list_for_each_entry(dep, &head, node)
+		acpihp_slot_change_state(dep->slot,
+					 ACPIHP_SLOT_STATE_CONFIGURING);
+
+	result = acpihp_drv_walk_devs(&head, DRV_OP_PRE_CONFIGURE,
+				      DEVICE_STATE_PRE_CONFIGURE, false);
+	if (!result)
+		result = acpihp_drv_walk_devs(&head, DRV_OP_CONFIGURE,
+					      DEVICE_STATE_CONFIGURED, false);
+	if (result)
+		post = ACPIHP_DEV_POST_CMD_ROLLBACK;
+	acpihp_drv_walk_devs_post(&head, DRV_OP_POST_CONFIGURE,
+				  DEVICE_STATE_CONNECTED, post, false);
+
+	list_for_each_entry(dep, &head, node)
+		acpihp_drv_update_slot_state(dep->slot);
+	acpihp_drv_sync_cancel(&head, result);
+	acpihp_drv_destroy_dependency_list(&head);
+
+	return result;
+}
+
+static int acpihp_drv_release(struct list_head *list)
+{
+	int result;
+	enum acpihp_dev_post_cmd post = ACPIHP_DEV_POST_CMD_COMMIT;
+
+	result = acpihp_drv_walk_devs(list, DRV_OP_PRE_RELEASE,
+				      DEVICE_STATE_PRE_RELEASE, true);
+	if (!result)
+		result = acpihp_drv_walk_devs(list, DRV_OP_RELEASE,
+					      DEVICE_STATE_RELEASED, true);
+	if (result)
+		post = ACPIHP_DEV_POST_CMD_ROLLBACK;
+	acpihp_drv_walk_devs_post(list, DRV_OP_POST_RELEASE,
+				  DEVICE_STATE_CONFIGURED, post, true);
+	acpihp_drv_sync_cancel(list, result);
+
+	return result;
+}
+
+static void __acpihp_drv_unconfigure(struct list_head *list)
+{
+	int result;
+
+	result = acpihp_drv_walk_devs(list, DRV_OP_PRE_UNCONFIGURE,
+				      DEVICE_STATE_PRE_UNCONFIGURE, true);
+	BUG_ON(result);
+	result = acpihp_drv_walk_devs(list, DRV_OP_UNCONFIGURE,
+				      DEVICE_STATE_CONNECTED, true);
+	BUG_ON(result);
+	result = acpihp_drv_walk_devs(list, DRV_OP_POST_UNCONFIGURE,
+				      DEVICE_STATE_CONNECTED, true);
+	BUG_ON(result);
+}
+
+/*
+ * To support better error recover and cancellation, unconfigure operations
+ * are splitted into three steps:
+ * 1) pre_release(): optional
+ * 2) release(): reclaim devices from system
+ * 3) post_release(): rollback if cancelled or failed to reclaim devices
+ * 4) pre_unconfigure(): optional
+ * 5) unconfigure(): remove devices from system
+ * 6) post_unconfigure(): free resources used by devices
+ */
+int acpihp_drv_unconfigure(struct list_head *list)
+{
+	int result;
+	struct list_head head;
+	struct acpihp_slot_dependency *dep;
+
+	result = acpihp_drv_filter_dependency_list(list, &head,
+						   ACPIHP_DRV_CMD_UNCONFIGURE);
+	if (result) {
+		ACPIHP_DEBUG("fails to filter dependency list.\n");
+		return -ENOMEM;
+	}
+
+	list_for_each_entry(dep, &head, node)
+		acpihp_slot_change_state(dep->slot,
+					 ACPIHP_SLOT_STATE_UNCONFIGURING);
+
+	result = acpihp_drv_release(&head);
+	if (!result)
+		__acpihp_drv_unconfigure(&head);
+
+	list_for_each_entry(dep, &head, node)
+		acpihp_drv_update_slot_state(dep->slot);
+
+	acpihp_drv_destroy_dependency_list(&head);
+
+	return result;
+}
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 15/28] ACPIHP: implement the state machine for ACPI hotplug slots
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (13 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 14/28] ACPIHP: configure/unconfigure system devices connecting to a hotplug slot Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 16/28] ACPIHP: implement sysfs interfaces for ACPI system device hotplug Jiang Liu
                   ` (13 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

This patch implements the core of the new ACPI hotplug framework,
a state machine for ACPI hotplug slots. The state machine is:
      <install>    <power on>      <connect>     <configure>
[ABSENT] <-> [PRESENT] <-> [POWERED] <-> [CONNECTED] <->[CONFIGURED]
     <uninstall>   <power off>    <disconnect>  <unconfigure>

It glues all logic together, including slot dependency, operation
cancellation, device configuration etc.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
Signed-off-by: Hanjun Guo <guohanjun@huawei.com>
---
 drivers/acpi/hotplug/Makefile        |    1 +
 drivers/acpi/hotplug/acpihp_drv.h    |    9 +
 drivers/acpi/hotplug/state_machine.c |  631 ++++++++++++++++++++++++++++++++++
 3 files changed, 641 insertions(+)
 create mode 100644 drivers/acpi/hotplug/state_machine.c

diff --git a/drivers/acpi/hotplug/Makefile b/drivers/acpi/hotplug/Makefile
index 6cb6aa1..0f43933 100644
--- a/drivers/acpi/hotplug/Makefile
+++ b/drivers/acpi/hotplug/Makefile
@@ -15,3 +15,4 @@ acpihp_drv-y					= drv_main.o
 acpihp_drv-y					+= dependency.o
 acpihp_drv-y					+= cancel.o
 acpihp_drv-y					+= configure.o
+acpihp_drv-y					+= state_machine.o
diff --git a/drivers/acpi/hotplug/acpihp_drv.h b/drivers/acpi/hotplug/acpihp_drv.h
index 8fa37be..fd94a1d 100644
--- a/drivers/acpi/hotplug/acpihp_drv.h
+++ b/drivers/acpi/hotplug/acpihp_drv.h
@@ -25,6 +25,9 @@
 #ifndef	__ACPIHP_DRV_H__
 #define	__ACPIHP_DRV_H__
 
+/* Timeout value to wait for firmware to power on the slot */
+#define ACPIHP_EVENT_WAIT_TIME  (100 * HZ)
+
 /* Commands to change state of a hotplug slot */
 enum acpihp_drv_cmd {
 	ACPIHP_DRV_CMD_NOOP = 0,
@@ -68,6 +71,9 @@ struct acpihp_slot_dependency {
 	u32				opcodes;
 };
 
+extern struct mutex state_machine_mutex;
+extern wait_queue_head_t acpihp_drv_event_wq;
+
 void acpihp_drv_get_data(struct acpihp_slot *slot,
 			 struct acpihp_slot_drv **data);
 int acpihp_drv_enumerate_devices(struct acpihp_slot *slot);
@@ -92,4 +98,7 @@ int acpihp_drv_cancel_wait(struct list_head *list);
 int acpihp_drv_configure(struct list_head *list);
 int acpihp_drv_unconfigure(struct list_head *list);
 
+/* The heart of the ACPI system device hotplug driver */
+int acpihp_drv_change_state(struct acpihp_slot *slot, enum acpihp_drv_cmd cmd);
+
 #endif	/* __ACPIHP_DRV_H__ */
diff --git a/drivers/acpi/hotplug/state_machine.c b/drivers/acpi/hotplug/state_machine.c
new file mode 100644
index 0000000..bcf4064
--- /dev/null
+++ b/drivers/acpi/hotplug/state_machine.c
@@ -0,0 +1,631 @@
+/*
+ * Copyright (C) 2012 Huawei Tech. Co., Ltd.
+ * Copyright (C) 2012 Jiang Liu <jiang.liu@huawei.com>
+ * Copyright (C) 2012 Hanjun Guo <guohanjun@huawei.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/mutex.h>
+#include <linux/wait.h>
+#include <acpi/acpi_hotplug.h>
+#include "acpihp_drv.h"
+
+/*
+ * Global lock to serialize manipulating of dependency list among hotplug slots
+ * to avoid deadlock among slots. The lock order is:
+ * 1) acquire state_machine_mutex;
+ * 2) acquire drv_data->op_mutex;
+ * 3) acquire slot->slot_mutex;
+ */
+DEFINE_MUTEX(state_machine_mutex);
+
+DECLARE_WAIT_QUEUE_HEAD(acpihp_drv_event_wq);
+
+static int acpihp_drv_lock_slot(struct acpihp_slot *slot)
+{
+	int retval = 0;
+	struct acpihp_slot_drv *drv_data;
+
+	acpihp_drv_get_data(slot, &drv_data);
+	if (!mutex_trylock(&drv_data->op_mutex)) {
+		ACPIHP_SLOT_DEBUG(slot, "slot is busy in state %d.\n",
+				  slot->state);
+		retval = -EBUSY;
+	}
+
+	return retval;
+}
+
+static void acpihp_drv_unlock_slot(struct acpihp_slot *slot)
+{
+	struct acpihp_slot_drv *drv_data;
+
+	acpihp_drv_get_data(slot, &drv_data);
+	BUG_ON(!mutex_is_locked(&drv_data->op_mutex));
+	mutex_unlock(&drv_data->op_mutex);
+}
+
+/*
+ * Lock all slots in the dependency list to serialize concurrent operations.
+ * Caller must hold state_machine_mutex.
+ */
+static int acpihp_drv_lock_slots(struct list_head *list,
+				 struct acpihp_slot *slot)
+{
+	struct acpihp_slot_dependency *dep;
+
+	list_for_each_entry(dep, list, node)
+		if (acpihp_drv_lock_slot(dep->slot))
+			goto unlock;
+
+	return 0;
+
+unlock:
+	list_for_each_entry_continue_reverse(dep, list, node)
+		acpihp_drv_unlock_slot(dep->slot);
+
+	return -EBUSY;
+}
+
+static void acpihp_drv_unlock_slots(struct list_head *list)
+{
+	struct acpihp_slot_dependency *dep;
+
+	list_for_each_entry(dep, list, node)
+		acpihp_drv_unlock_slot(dep->slot);
+}
+
+static bool acpihp_drv_is_ancestor(struct acpihp_slot *ancestor,
+				   struct acpihp_slot *slot)
+{
+	while (slot) {
+		if (slot->parent == ancestor)
+			return true;
+		slot = slot->parent;
+	};
+
+	return false;
+}
+
+/*
+ * Check whether the command is valid according to current slot state,
+ * and get required operations for this slot if command is valid.
+ */
+static int acpihp_drv_validate_transition(struct acpihp_slot_dependency *dep,
+					  struct acpihp_slot *target,
+					  enum acpihp_drv_cmd cmd)
+{
+	u32 op1, op2;
+	struct acpihp_slot *slot = dep->slot;
+
+	if (slot->state <= ACPIHP_SLOT_STATE_UNKNOWN ||
+	    slot->state >= ACPIHP_SLOT_STATE_MAX) {
+		ACPIHP_SLOT_DEBUG(slot, "invalid state %d.\n", slot->state);
+		return -EINVAL;
+	} else if (slot->state >= ACPIHP_SLOT_STATE_POWERING_ON) {
+		/*
+		 * This shouldn't happen, transcendant states are protected
+		 * by slot->op_mutex.
+		 */
+		BUG_ON(slot->state);
+		return -EBUSY;
+	}
+
+	op1 = op2 = ACPIHP_DRV_CMD_NOOP;
+	dep->opcodes = ACPIHP_DRV_CMD_NOOP;
+
+	/*
+	 * To be compatible with legacy OSes, the PCI host bridges built into
+	 * physical processor may be hosted directly under \\__SB instead of
+	 * under the CONTAINER device corresponding to physical processor.
+	 * That's really a corner case to deal with.
+	 */
+	switch (cmd) {
+	case ACPIHP_DRV_CMD_POWERON:
+		if (slot->state == ACPIHP_SLOT_STATE_ABSENT)
+			return -EINVAL;
+		else if (slot->state == ACPIHP_SLOT_STATE_PRESENT)
+			dep->opcodes = ACPIHP_DRV_CMD_POWERON;
+		break;
+
+	case ACPIHP_DRV_CMD_CONNECT:
+		/*
+		 * Its parent must have already been connected when connecting
+		 * a slot, otherwise the device tree topology becomes incorrect.
+		 */
+		if (target == slot || acpihp_drv_is_ancestor(slot, target))
+			op2 = ACPIHP_DRV_CMD_CONNECT;
+
+		if (slot->state == ACPIHP_SLOT_STATE_ABSENT)
+			return -EINVAL;
+		else if (slot->state == ACPIHP_SLOT_STATE_PRESENT)
+			dep->opcodes = ACPIHP_DRV_CMD_POWERON | op2;
+		else if (slot->state == ACPIHP_SLOT_STATE_POWERED)
+			dep->opcodes = op2;
+		break;
+
+	case ACPIHP_DRV_CMD_CONFIGURE:
+		/* Only CONFIGURE the requested slot */
+		if (slot == target)
+			op1 = ACPIHP_DRV_CMD_CONFIGURE;
+		/*
+		 * Its parent must have already been connected when configuring
+		 * a slot, otherwise the device tree topology becomes incorrect.
+		 */
+		if (target == slot || acpihp_drv_is_ancestor(slot, target))
+			op2 = ACPIHP_DRV_CMD_CONNECT;
+
+		if (slot->state == ACPIHP_SLOT_STATE_ABSENT)
+			return -EINVAL;
+		else if (slot->state == ACPIHP_SLOT_STATE_PRESENT)
+			dep->opcodes = ACPIHP_DRV_CMD_POWERON | op1 | op2;
+		else if (slot->state == ACPIHP_SLOT_STATE_POWERED)
+			dep->opcodes = op1 | op2;
+		else if (slot->state == ACPIHP_SLOT_STATE_CONNECTED)
+			dep->opcodes = op1;
+		break;
+
+	case ACPIHP_DRV_CMD_UNCONFIGURE:
+		/* Only UNCONFIGURE the requested slot */
+		if (slot->state == ACPIHP_SLOT_STATE_CONFIGURED &&
+		    slot == target)
+			dep->opcodes = ACPIHP_DRV_CMD_UNCONFIGURE;
+		break;
+
+	case ACPIHP_DRV_CMD_DISCONNECT:
+		/*
+		 * all descedant slots must be unconfigured/disconnected
+		 * when disconnecting a slot.
+		 */
+		if (target == slot || acpihp_drv_is_ancestor(target, slot)) {
+			op1 = ACPIHP_DRV_CMD_UNCONFIGURE;
+			op2 = ACPIHP_DRV_CMD_DISCONNECT;
+		}
+
+		if (slot->state == ACPIHP_SLOT_STATE_CONFIGURED)
+			dep->opcodes = op1 | op2;
+		else if (slot->state == ACPIHP_SLOT_STATE_CONNECTED)
+			dep->opcodes = op2;
+		break;
+
+	case ACPIHP_DRV_CMD_POWEROFF:
+		/*
+		 * All slots have dependency on the target slot must be
+		 * powered off when powering the target slot off.
+		 */
+		if (slot->state == ACPIHP_SLOT_STATE_CONFIGURED)
+			dep->opcodes = ACPIHP_DRV_CMD_UNCONFIGURE |
+				       ACPIHP_DRV_CMD_DISCONNECT |
+				       ACPIHP_DRV_CMD_POWEROFF;
+		else if (slot->state == ACPIHP_SLOT_STATE_CONNECTED)
+			dep->opcodes = ACPIHP_DRV_CMD_DISCONNECT |
+				       ACPIHP_DRV_CMD_POWEROFF;
+		else if (slot->state == ACPIHP_SLOT_STATE_POWERED)
+			dep->opcodes = ACPIHP_DRV_CMD_POWEROFF;
+		break;
+
+	default:
+		ACPIHP_SLOT_DEBUG(slot, "invalid command %d.\n", cmd);
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int acpihp_drv_validate_command(struct list_head *list,
+		struct acpihp_slot *target, enum acpihp_drv_cmd cmd)
+{
+	int retval;
+	struct acpihp_slot *slot;
+	struct acpihp_slot_dependency *dep;
+
+	list_for_each_entry(dep, list, node) {
+		slot = dep->slot;
+		acpihp_drv_update_slot_status(slot);
+
+		retval = acpihp_drv_validate_transition(dep, target, cmd);
+		if (retval) {
+			ACPIHP_SLOT_DEBUG(slot,
+				"Invalid cmd for slot in state %d.\n",
+				slot->state);
+			return retval;
+		}
+
+		/*
+		 * Check whether the slot is in good shape if we need to
+		 * operate on it.
+		 */
+		if (dep->opcodes &&
+		    acpihp_slot_get_flag(slot, ACPIHP_SLOT_FLAG_FAULT)) {
+			ACPIHP_SLOT_WARN(slot,
+					 "slot has been marked as faulty.\n");
+			return -ENXIO;
+		} else if ((dep->opcodes & ACPIHP_DRV_CMD_UNCONFIGURE) &&
+		    acpihp_slot_get_flag(slot, ACPIHP_SLOT_FLAG_IRREMOVABLE)) {
+			ACPIHP_SLOT_WARN(slot, "slot is busy.\n");
+			return -EBUSY;
+		}
+	}
+
+	return 0;
+}
+
+static int acpihp_drv_pre_execute(struct acpihp_slot *slot,
+				  enum acpihp_drv_cmd cmd,
+				  struct list_head **head)
+{
+	int retval;
+	struct list_head *list;
+	struct acpihp_slot_drv *drv_data;
+
+	mutex_lock(&state_machine_mutex);
+
+	acpihp_drv_get_data(slot, &drv_data);
+	*head = list = &drv_data->depend_list;
+
+	if (cmd == ACPIHP_DRV_CMD_CANCEL) {
+		/*
+		 * Set cancellation flags on all affected slots.
+		 * All affected slots should already be on drv_data->depend_list
+		 * if there's inprogress operation for the slot.
+		 *
+		 * state_machine_mutex must be held to serialize calls to
+		 *	acpihp_drv_cancel_init(),
+		 *	acpihp_drv_cancel_start(),
+		 *	acpihp_drv_cancel_fini(),
+		 */
+		retval = acpihp_drv_cancel_start(&drv_data->depend_list);
+		goto out;
+	}
+
+	if (mutex_is_locked(&drv_data->op_mutex)) {
+		ACPIHP_DEBUG("slot %s is busy.\n", slot->name);
+		retval = -EBUSY;
+		goto out;
+	}
+
+	INIT_LIST_HEAD(list);
+	retval = acpihp_drv_generate_dependency_list(slot, list, cmd);
+	if (retval) {
+		ACPIHP_DEBUG("fails to get dependency list for slot %s.\n",
+			     slot->name);
+		goto out;
+	}
+
+	retval = acpihp_drv_lock_slots(list, slot);
+	if (retval) {
+		acpihp_drv_destroy_dependency_list(list);
+		goto out;
+	}
+
+	retval = acpihp_drv_validate_command(list, slot, cmd);
+	if (retval) {
+		acpihp_drv_unlock_slots(list);
+		acpihp_drv_destroy_dependency_list(list);
+		goto out;
+	}
+
+	acpihp_drv_cancel_init(list);
+
+out:
+	mutex_unlock(&state_machine_mutex);
+
+	return retval;
+}
+
+static void acpihp_drv_post_execute(struct list_head *list,
+				    enum acpihp_drv_cmd cmd)
+{
+	if (cmd == ACPIHP_DRV_CMD_CANCEL)
+		return;
+
+	mutex_lock(&state_machine_mutex);
+	if (list && !list_empty(list)) {
+		acpihp_drv_cancel_fini(list);
+		acpihp_drv_unlock_slots(list);
+		acpihp_drv_destroy_dependency_list(list);
+	}
+	mutex_unlock(&state_machine_mutex);
+}
+
+static int acpihp_drv_poweron_slot(struct acpihp_slot *slot)
+{
+	acpi_status status;
+	struct acpihp_slot_drv *drv_data;
+
+	if (acpihp_slot_powered(slot))
+		return 0;
+
+	acpihp_drv_get_data(slot, &drv_data);
+	drv_data->event_flag = ACPIHP_DRV_EVENT_FROM_OS;
+
+	status = acpihp_slot_poweron(slot);
+	if (ACPI_FAILURE(status)) {
+		ACPIHP_SLOT_WARN(slot, "fails to power on slot.\n");
+		if (status == AE_SUPPORT)
+			return -ENOSYS;
+		else
+			return -ENXIO;
+	}
+
+	wait_event_timeout(acpihp_drv_event_wq, acpihp_slot_powered(slot),
+			   ACPIHP_EVENT_WAIT_TIME);
+	if (!acpihp_slot_powered(slot)) {
+		ACPIHP_SLOT_WARN(slot, "fails to power on slot.\n");
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static int acpihp_drv_poweron(struct list_head *list)
+{
+	int retval = 0;
+	struct acpihp_slot_dependency *dep;
+
+	list_for_each_entry(dep, list, node) {
+		if (!(dep->opcodes & ACPIHP_DRV_CMD_POWERON))
+			continue;
+
+		acpihp_slot_change_state(dep->slot,
+					 ACPIHP_SLOT_STATE_POWERING_ON);
+		retval = acpihp_drv_poweron_slot(dep->slot);
+		if (!retval) {
+			ACPIHP_SLOT_DEBUG(dep->slot, "succeed to power on!\n");
+			acpihp_slot_change_state(dep->slot,
+						 ACPIHP_SLOT_STATE_POWERED);
+		} else {
+			acpihp_slot_change_state(dep->slot,
+						 ACPIHP_SLOT_STATE_PRESENT);
+			if (retval != -ENOSYS)
+				acpihp_slot_set_flag(dep->slot,
+						     ACPIHP_SLOT_FLAG_FAULT);
+			break;
+		}
+	}
+
+	return retval;
+}
+
+static int acpihp_drv_poweroff_slot(struct acpihp_slot *slot)
+{
+	acpi_status status;
+
+	if (acpihp_slot_powered(slot))
+		return 0;
+
+	status = acpihp_slot_poweroff(slot);
+	if (ACPI_FAILURE(status)) {
+		ACPIHP_SLOT_WARN(slot, "fails to power off slot.\n");
+		if (status == AE_SUPPORT)
+			return -ENOSYS;
+		else
+			return -ENXIO;
+	}
+
+	if (acpihp_slot_powered(slot)) {
+		ACPIHP_SLOT_WARN(slot, "fails to power off slot.\n");
+		return -ENXIO;
+	}
+
+	return 0;
+}
+
+static int acpihp_drv_poweroff(struct list_head *list)
+{
+	int retval = 0;
+	struct acpihp_slot_dependency *dep;
+
+	list_for_each_entry(dep, list, node) {
+		if (!(dep->opcodes & ACPIHP_DRV_CMD_POWEROFF))
+			continue;
+
+		acpihp_slot_change_state(dep->slot,
+					 ACPIHP_SLOT_STATE_POWERING_OFF);
+		retval = acpihp_drv_poweroff_slot(dep->slot);
+		if (!retval) {
+			ACPIHP_SLOT_DEBUG(dep->slot,
+					  "succeed to power off slot!\n");
+			acpihp_slot_change_state(dep->slot,
+						 ACPIHP_SLOT_STATE_PRESENT);
+		} else {
+			acpihp_slot_change_state(dep->slot,
+						 ACPIHP_SLOT_STATE_POWERED);
+			if (retval != -ENOSYS)
+				acpihp_slot_set_flag(dep->slot,
+						     ACPIHP_SLOT_FLAG_FAULT);
+			break;
+		}
+	}
+
+	return retval;
+}
+
+static int acpihp_drv_connect_slot(struct acpihp_slot *slot)
+{
+	int retval;
+
+	retval = acpihp_add_devices(slot->handle, NULL);
+	if (retval)
+		ACPIHP_SLOT_DEBUG(slot,
+				  "fails to add ACPI devices for slot.\n");
+	else {
+		retval = acpihp_drv_enumerate_devices(slot);
+		if (retval)
+			ACPIHP_SLOT_DEBUG(slot,
+				"fails to enumerate device for slot.\n");
+	}
+
+	return retval;
+}
+
+static int acpihp_drv_connect(struct list_head *list)
+{
+	int retval = 0;
+	struct acpihp_slot_dependency *dep;
+
+	list_for_each_entry(dep, list, node) {
+		if (!(dep->opcodes & ACPIHP_DRV_CMD_CONNECT))
+			continue;
+
+		acpihp_slot_change_state(dep->slot,
+					 ACPIHP_SLOT_STATE_CONNECTING);
+		retval = acpihp_drv_connect_slot(dep->slot);
+		if (!retval) {
+			ACPIHP_SLOT_DEBUG(dep->slot,
+					  "succeed to connect slot!\n");
+			acpihp_slot_change_state(dep->slot,
+						 ACPIHP_SLOT_STATE_CONNECTED);
+		} else {
+			acpihp_drv_update_slot_state(dep->slot);
+			break;
+		}
+	}
+
+	return retval;
+}
+
+static int acpihp_drv_disconnect_slot(struct acpihp_slot *slot)
+{
+	int retval = 0;
+	enum acpihp_dev_type i;
+	struct acpi_device *device = NULL;
+
+	/* remove all devices connecting to slot */
+	for (i = ACPIHP_DEV_TYPE_UNKNOWN; i < ACPIHP_DEV_TYPE_MAX; i++) {
+		retval = acpihp_remove_device_list(&slot->dev_lists[i]);
+		if (retval) {
+			ACPIHP_SLOT_DEBUG(slot,
+					  "fails to remove ACPI devices.\n");
+			return retval;
+		}
+	}
+
+	/* remove ACPI devices */
+	retval = acpi_bus_get_device(slot->handle, &device);
+	if (!retval && device) {
+		retval = acpi_bus_trim(device, 1);
+		if (retval)
+			ACPIHP_SLOT_WARN(slot,
+					 "fails to remove ACPI devices.\n");
+	} else
+		ACPIHP_SLOT_WARN(slot, "fails to get ACPI device for slot.\n");
+
+	return retval;
+}
+
+static int acpihp_drv_disconnect(struct list_head *list)
+{
+	int retval = 0;
+	struct acpihp_slot_dependency *dep;
+
+	list_for_each_entry(dep, list, node) {
+		if (!(dep->opcodes & ACPIHP_DRV_CMD_DISCONNECT))
+			continue;
+
+		acpihp_slot_change_state(dep->slot,
+					 ACPIHP_SLOT_STATE_DISCONNECTING);
+		retval = acpihp_drv_disconnect_slot(dep->slot);
+		if (!retval) {
+			ACPIHP_SLOT_DEBUG(dep->slot,
+					  "succeed to disconnect slot!\n");
+			acpihp_slot_change_state(dep->slot,
+						 ACPIHP_SLOT_STATE_POWERED);
+		} else {
+			acpihp_drv_update_slot_state(dep->slot);
+			break;
+		}
+	}
+
+	return retval;
+}
+
+static int acpihp_drv_execute(struct list_head *list, enum acpihp_drv_cmd cmd)
+{
+	int retval = 0;
+	bool connect = false, configure = false;
+	bool disconnect = false, poweroff = false;
+
+	if (!list || list_empty(list)) {
+		ACPIHP_DEBUG("slot dependency list is NULL or empty!\n");
+		retval = -EINVAL;
+		goto out;
+	}
+
+	switch (cmd) {
+	case ACPIHP_DRV_CMD_CONFIGURE:
+		configure = true;
+		/* fall through */
+	case ACPIHP_DRV_CMD_CONNECT:
+		connect = true;
+		/* fall through */
+	case ACPIHP_DRV_CMD_POWERON:
+		retval = acpihp_drv_poweron(list);
+		if (!retval && connect)
+			retval = acpihp_drv_connect(list);
+		if (!retval && configure)
+			retval = acpihp_drv_configure(list);
+		break;
+
+	case ACPIHP_DRV_CMD_POWEROFF:
+		poweroff = true;
+		/* fall through */
+	case ACPIHP_DRV_CMD_DISCONNECT:
+		disconnect = true;
+		/* fall through */
+	case ACPIHP_DRV_CMD_UNCONFIGURE:
+		retval = acpihp_drv_unconfigure(list);
+		if (!retval && disconnect)
+			retval = acpihp_drv_disconnect(list);
+		if (!retval && poweroff)
+			retval = acpihp_drv_poweroff(list);
+		break;
+
+	case ACPIHP_DRV_CMD_CANCEL:
+		retval = acpihp_drv_cancel_wait(list);
+		break;
+
+	default:
+		ACPIHP_DEBUG("unsupported command %d.\n", cmd);
+		retval = -EINVAL;
+		break;
+	}
+
+out:
+	return retval;
+}
+
+/*
+ * The heart of ACPI based system device hotplug driver, which drivers the
+ * slot state machine according to commands.
+ */
+int acpihp_drv_change_state(struct acpihp_slot *slot, enum acpihp_drv_cmd cmd)
+{
+	int retval;
+	struct list_head *list = NULL;
+
+	retval = acpihp_drv_pre_execute(slot, cmd, &list);
+	if (!retval) {
+		retval = acpihp_drv_execute(list, cmd);
+		acpihp_drv_post_execute(list, cmd);
+	}
+
+	return retval;
+}
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 16/28] ACPIHP: implement sysfs interfaces for ACPI system device hotplug
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (14 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 15/28] ACPIHP: implement the state machine for ACPI hotplug slots Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 17/28] ACPIHP: block ACPI device driver unloading when executing hotplug operations Jiang Liu
                   ` (12 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

This patch implements following sysfs interfaces for ACPI system device
hotplug.

SLOT/enable: (It should be named 'power', but that causes name conflicts.)
power on the hotplug slot:
	echo 1 > enable
power off the hotplug slot:
	echo 0 > enable:

SLOT/connect:
create ACPI devices for system devices connecting to a slot:
	echo 1 > connect
remove ACPI devices for system devices connecting to a slot:
	echo 0 > connect

SLOT/configure:
add system devices into running system:
	echo 1 > configure
remove system devices from running system:
	echo 0 > configure

SLOT/dependency:
show dependencies among hotplug slots:
	cat dependency

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
Signed-off-by: Hanjun Guo <guohanjun@huawei.com>
---
 drivers/acpi/hotplug/Makefile     |    1 +
 drivers/acpi/hotplug/acpihp_drv.h |    3 +
 drivers/acpi/hotplug/drv_main.c   |    5 +
 drivers/acpi/hotplug/sysfs.c      |  245 +++++++++++++++++++++++++++++++++++++
 4 files changed, 254 insertions(+)
 create mode 100644 drivers/acpi/hotplug/sysfs.c

diff --git a/drivers/acpi/hotplug/Makefile b/drivers/acpi/hotplug/Makefile
index 0f43933..57e348a 100644
--- a/drivers/acpi/hotplug/Makefile
+++ b/drivers/acpi/hotplug/Makefile
@@ -16,3 +16,4 @@ acpihp_drv-y					+= dependency.o
 acpihp_drv-y					+= cancel.o
 acpihp_drv-y					+= configure.o
 acpihp_drv-y					+= state_machine.o
+acpihp_drv-y					+= sysfs.o
diff --git a/drivers/acpi/hotplug/acpihp_drv.h b/drivers/acpi/hotplug/acpihp_drv.h
index fd94a1d..dcfa9ac 100644
--- a/drivers/acpi/hotplug/acpihp_drv.h
+++ b/drivers/acpi/hotplug/acpihp_drv.h
@@ -101,4 +101,7 @@ int acpihp_drv_unconfigure(struct list_head *list);
 /* The heart of the ACPI system device hotplug driver */
 int acpihp_drv_change_state(struct acpihp_slot *slot, enum acpihp_drv_cmd cmd);
 
+int acpihp_drv_create_sysfs(struct acpihp_slot *slot);
+void acpihp_drv_remove_sysfs(struct acpihp_slot *slot);
+
 #endif	/* __ACPIHP_DRV_H__ */
diff --git a/drivers/acpi/hotplug/drv_main.c b/drivers/acpi/hotplug/drv_main.c
index 3e2a52a..c474546 100644
--- a/drivers/acpi/hotplug/drv_main.c
+++ b/drivers/acpi/hotplug/drv_main.c
@@ -284,6 +284,10 @@ static int acpihp_drv_slot_add(struct device *dev, struct class_interface *intf)
 		return -ENOMEM;
 	}
 
+	if (acpihp_drv_create_sysfs(slot))
+		ACPIHP_SLOT_DEBUG(slot,
+			"fails to create sysfs interfaces, some functions will not be available to user.\n");
+
 	return 0;
 }
 
@@ -294,6 +298,7 @@ static void acpihp_drv_intf_remove(struct device *dev,
 	struct acpihp_slot *slot =
 			container_of(dev, struct acpihp_slot, dev);
 
+	acpihp_drv_remove_sysfs(slot);
 	acpihp_drv_uninstall_handler(slot);
 	acpihp_drv_remove_devices(slot);
 	acpihp_slot_detach_drv_data(slot, intf, (void **)&drv_data);
diff --git a/drivers/acpi/hotplug/sysfs.c b/drivers/acpi/hotplug/sysfs.c
new file mode 100644
index 0000000..fd8819b
--- /dev/null
+++ b/drivers/acpi/hotplug/sysfs.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2012 Huawei Tech. Co., Ltd.
+ * Copyright (C) 2012 Jiang Liu <jiang.liu@huawei.com>
+ * Copyright (C) 2012 Hanjun Guo <guohanjun@huawei.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/device.h>
+#include <linux/mutex.h>
+#include <acpi/acpi_hotplug.h>
+#include "acpihp_drv.h"
+
+static ssize_t acpihp_drv_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count,
+		enum acpihp_drv_cmd op_set, enum acpihp_drv_cmd op_clr)
+{
+	unsigned long val;
+	ssize_t result = kstrtoul(buf, 0, &val);
+	struct acpihp_slot *slot = container_of(dev, struct acpihp_slot, dev);
+
+	if (result < 0)
+		return result;
+
+	if (!capable(CAP_SYS_ADMIN))
+		return -EPERM;
+
+	if (val)
+		result = acpihp_drv_change_state(slot, op_set);
+	else
+		result = acpihp_drv_change_state(slot, op_clr);
+
+	return result < 0 ? result : count;
+}
+
+static inline ssize_t acpihp_drv_print_bool(char *page, bool flag)
+{
+	return snprintf(page, PAGE_SIZE, flag ? "1\n" : "0\n");
+}
+
+static ssize_t acpihp_drv_power_show(struct device *dev,
+		struct device_attribute *attr, char *page)
+{
+	bool flag;
+	struct acpihp_slot *slot = container_of(dev, struct acpihp_slot, dev);
+
+	acpihp_drv_update_slot_state(slot);
+	mutex_lock(&slot->slot_mutex);
+	switch (slot->state) {
+	case ACPIHP_SLOT_STATE_POWERED:
+	case ACPIHP_SLOT_STATE_CONNECTED:
+	case ACPIHP_SLOT_STATE_CONFIGURED:
+	case ACPIHP_SLOT_STATE_POWERING_ON:
+	case ACPIHP_SLOT_STATE_POWERING_OFF:
+	case ACPIHP_SLOT_STATE_CONNECTING:
+	case ACPIHP_SLOT_STATE_DISCONNECTING:
+	case ACPIHP_SLOT_STATE_CONFIGURING:
+	case ACPIHP_SLOT_STATE_UNCONFIGURING:
+		flag = true;
+		break;
+	default:
+		flag = false;
+		break;
+	}
+	mutex_unlock(&slot->slot_mutex);
+
+	return acpihp_drv_print_bool(page, flag);
+}
+
+static ssize_t acpihp_drv_power_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	return acpihp_drv_store(dev, attr, buf, count,
+			ACPIHP_DRV_CMD_POWERON, ACPIHP_DRV_CMD_POWEROFF);
+}
+
+/* It should be named as 'power', but that will cause name conflics. */
+DEVICE_ATTR(enable, S_IRUSR | S_IWUSR,
+	    &acpihp_drv_power_show, &acpihp_drv_power_store);
+
+static ssize_t acpihp_drv_connect_show(struct device *dev,
+		struct device_attribute *attr, char *page)
+{
+	bool flag;
+	struct acpihp_slot *slot = container_of(dev, struct acpihp_slot, dev);
+
+	acpihp_drv_update_slot_state(slot);
+	mutex_lock(&slot->slot_mutex);
+	switch (slot->state) {
+	case ACPIHP_SLOT_STATE_CONNECTED:
+	case ACPIHP_SLOT_STATE_CONFIGURED:
+	case ACPIHP_SLOT_STATE_CONNECTING:
+	case ACPIHP_SLOT_STATE_DISCONNECTING:
+	case ACPIHP_SLOT_STATE_CONFIGURING:
+	case ACPIHP_SLOT_STATE_UNCONFIGURING:
+		flag = true;
+		break;
+	default:
+		flag = false;
+		break;
+	}
+	mutex_unlock(&slot->slot_mutex);
+
+	return acpihp_drv_print_bool(page, flag);
+}
+
+static ssize_t acpihp_drv_connect_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	return acpihp_drv_store(dev, attr, buf, count,
+			ACPIHP_DRV_CMD_CONNECT, ACPIHP_DRV_CMD_DISCONNECT);
+}
+
+DEVICE_ATTR(connect, S_IRUSR | S_IWUSR,
+	    &acpihp_drv_connect_show, &acpihp_drv_connect_store);
+
+static ssize_t acpihp_drv_configure_show(struct device *dev,
+		struct device_attribute *attr, char *page)
+{
+	bool flag;
+	struct acpihp_slot *slot = container_of(dev, struct acpihp_slot, dev);
+
+	acpihp_drv_update_slot_state(slot);
+	mutex_lock(&slot->slot_mutex);
+	switch (slot->state) {
+	case ACPIHP_SLOT_STATE_CONFIGURED:
+	case ACPIHP_SLOT_STATE_CONFIGURING:
+	case ACPIHP_SLOT_STATE_UNCONFIGURING:
+		flag = true;
+		break;
+	default:
+		flag = false;
+		break;
+	}
+	mutex_unlock(&slot->slot_mutex);
+
+	return acpihp_drv_print_bool(page, flag);
+}
+
+static ssize_t acpihp_drv_configure_store(struct device *dev,
+		struct device_attribute *attr, const char *buf, size_t count)
+{
+	return acpihp_drv_store(dev, attr, buf, count,
+			ACPIHP_DRV_CMD_CONFIGURE, ACPIHP_DRV_CMD_UNCONFIGURE);
+}
+
+DEVICE_ATTR(configure, S_IRUSR | S_IWUSR,
+	    &acpihp_drv_configure_show, &acpihp_drv_configure_store);
+
+static ssize_t acpihp_drv_dependency_show(struct device *dev,
+		struct device_attribute *attr, char *page)
+{
+	int ret;
+	char *p, *end;
+	struct list_head list;
+	enum acpihp_drv_cmd cmd;
+	struct acpihp_slot_dependency *dep;
+	struct acpihp_slot *slot = container_of(dev, struct acpihp_slot, dev);
+
+	INIT_LIST_HEAD(&list);
+	mutex_lock(&state_machine_mutex);
+	cmd = acpihp_slot_powered(slot) ? ACPIHP_DRV_CMD_POWEROFF :
+					  ACPIHP_DRV_CMD_POWERON;
+	ret = acpihp_drv_generate_dependency_list(slot, &list, cmd);
+	if (ret) {
+		ret = -ENXIO;
+	} else {
+		p = page;
+		end = page + PAGE_SIZE;
+
+		list_for_each_entry(dep, &list, node) {
+			if (dep->slot == slot)
+				continue;
+			if (p + strlen(dep->slot->name) + 2 >= end)
+				break;
+			p += snprintf(p, end - p, "%s\n", dep->slot->name);
+		}
+
+		acpihp_drv_destroy_dependency_list(&list);
+		ret = p - page;
+	}
+	mutex_unlock(&state_machine_mutex);
+
+	return ret;
+}
+
+static DEVICE_ATTR(dependency, S_IRUSR | S_IWUSR,
+		   &acpihp_drv_dependency_show, NULL);
+
+int acpihp_drv_create_sysfs(struct acpihp_slot *slot)
+{
+	int retval;
+	struct device *dev = &slot->dev;
+
+	retval = device_create_file(dev, &dev_attr_enable);
+	if (retval)
+		goto out;
+	retval = device_create_file(dev, &dev_attr_connect);
+	if (retval)
+		goto out1;
+	retval = device_create_file(dev, &dev_attr_configure);
+	if (retval)
+		goto out2;
+	retval = device_create_file(dev, &dev_attr_dependency);
+	if (retval)
+		goto out3;
+
+	return 0;
+
+out3:
+	device_remove_file(dev, &dev_attr_configure);
+out2:
+	device_remove_file(dev, &dev_attr_connect);
+out1:
+	device_remove_file(dev, &dev_attr_enable);
+out:
+	ACPIHP_SLOT_DEBUG(slot, "fails to create sysfs interfaces for slot.\n");
+	return retval;
+}
+
+void acpihp_drv_remove_sysfs(struct acpihp_slot *slot)
+{
+	struct device *dev = &slot->dev;
+
+	device_remove_file(dev, &dev_attr_dependency);
+	device_remove_file(dev, &dev_attr_configure);
+	device_remove_file(dev, &dev_attr_connect);
+	device_remove_file(dev, &dev_attr_enable);
+}
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 17/28] ACPIHP: block ACPI device driver unloading when executing hotplug operations
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (15 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 16/28] ACPIHP: implement sysfs interfaces for ACPI system device hotplug Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 18/28] ACPIHP/container: change ACPI container driver to support new hotplug framework Jiang Liu
                   ` (11 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

The ACPI hotplug driver depends on ACPI device drivers to configure or
unconfigure ACPI devices. It will confuse the hotplug driver if an ACPI
device driver is unloaded when there are ongoing hotplug operations.
So block ACPI device driver unbinding/unloading by holding the device
lock when there are ongoing hotplug operations.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
---
 drivers/acpi/hotplug/configure.c |   18 ++++++++++++++++--
 drivers/acpi/hotplug/device.c    |    8 --------
 2 files changed, 16 insertions(+), 10 deletions(-)

diff --git a/drivers/acpi/hotplug/configure.c b/drivers/acpi/hotplug/configure.c
index 346b976..75e8752 100644
--- a/drivers/acpi/hotplug/configure.c
+++ b/drivers/acpi/hotplug/configure.c
@@ -37,7 +37,9 @@ enum config_op_code {
 	DRV_OP_POST_RELEASE,
 	DRV_OP_PRE_UNCONFIGURE,
 	DRV_OP_UNCONFIGURE,
-	DRV_OP_POST_UNCONFIGURE
+	DRV_OP_POST_UNCONFIGURE,
+	DRV_OP_LOCK_DEVICE,
+	DRV_OP_UNLOCK_DEVICE,
 };
 
 /* All devices will be configured in following order. */
@@ -102,6 +104,12 @@ static int acpihp_drv_invoke_method(enum config_op_code  opcode,
 		return acpihp_dev_unconfigure(dev);
 	case DRV_OP_POST_UNCONFIGURE:
 		return acpihp_dev_post_unconfigure(dev);
+	case DRV_OP_LOCK_DEVICE:
+		device_lock(&dev->dev);
+		return 0;
+	case DRV_OP_UNLOCK_DEVICE:
+		device_unlock(&dev->dev);
+		return 0;
 	default:
 		BUG_ON(opcode);
 		return -ENOSYS;
@@ -126,7 +134,9 @@ static int acpihp_drv_call_method(enum config_op_code opcode,
 		result = acpihp_drv_invoke_method(opcode, slot, acpi_dev, 0);
 		if (result)
 			break;
-		acpihp_drv_update_dev_state(np, state);
+		if (opcode != DRV_OP_LOCK_DEVICE &&
+		    opcode != DRV_OP_UNLOCK_DEVICE)
+			acpihp_drv_update_dev_state(np, state);
 	}
 	klist_iter_exit(&iter);
 
@@ -248,6 +258,7 @@ int acpihp_drv_configure(struct list_head *list)
 		acpihp_slot_change_state(dep->slot,
 					 ACPIHP_SLOT_STATE_CONFIGURING);
 
+	acpihp_drv_walk_devs(&head, DRV_OP_LOCK_DEVICE, 0, false);
 	result = acpihp_drv_walk_devs(&head, DRV_OP_PRE_CONFIGURE,
 				      DEVICE_STATE_PRE_CONFIGURE, false);
 	if (!result)
@@ -257,6 +268,7 @@ int acpihp_drv_configure(struct list_head *list)
 		post = ACPIHP_DEV_POST_CMD_ROLLBACK;
 	acpihp_drv_walk_devs_post(&head, DRV_OP_POST_CONFIGURE,
 				  DEVICE_STATE_CONNECTED, post, false);
+	acpihp_drv_walk_devs(&head, DRV_OP_UNLOCK_DEVICE, 0, false);
 
 	list_for_each_entry(dep, &head, node)
 		acpihp_drv_update_slot_state(dep->slot);
@@ -327,9 +339,11 @@ int acpihp_drv_unconfigure(struct list_head *list)
 		acpihp_slot_change_state(dep->slot,
 					 ACPIHP_SLOT_STATE_UNCONFIGURING);
 
+	acpihp_drv_walk_devs(&head, DRV_OP_LOCK_DEVICE, 0, false);
 	result = acpihp_drv_release(&head);
 	if (!result)
 		__acpihp_drv_unconfigure(&head);
+	acpihp_drv_walk_devs(&head, DRV_OP_UNLOCK_DEVICE, 0, false);
 
 	list_for_each_entry(dep, &head, node)
 		acpihp_drv_update_slot_state(dep->slot);
diff --git a/drivers/acpi/hotplug/device.c b/drivers/acpi/hotplug/device.c
index d566522..90666f8 100644
--- a/drivers/acpi/hotplug/device.c
+++ b/drivers/acpi/hotplug/device.c
@@ -35,7 +35,6 @@ int acpihp_dev_get_info(struct acpi_device *device,
 
 	acpihp_dev_get_type(device->handle, &info->type);
 
-	device_lock(&device->dev);
 	if (device->driver && device->driver->ops.hp_ops &&
 	    device->driver->ops.hp_ops->get_info)
 		ret = device->driver->ops.hp_ops->get_info(device, info);
@@ -51,7 +50,6 @@ int acpihp_dev_get_info(struct acpi_device *device,
 
 	if (device->driver)
 		info->status |= ACPIHP_DEV_STATUS_ATTACHED;
-	device_unlock(&device->dev);
 
 	return ret;
 }
@@ -62,14 +60,12 @@ int acpihp_dev_##method(struct acpi_device *device, type val) \
 { \
 	int ret; \
 	BUG_ON(device == NULL); \
-	device_lock(&device->dev); \
 	if (!device->driver || !device->driver->ops.hp_ops) \
 		ret = (err); \
 	else if (!device->driver->ops.hp_ops->method) \
 		ret = (def); \
 	else \
 		ret = device->driver->ops.hp_ops->method(device, val); \
-	device_unlock(&device->dev); \
 	return ret; \
 } \
 EXPORT_SYMBOL_GPL(acpihp_dev_##method)
@@ -79,14 +75,12 @@ int acpihp_dev_##method(struct acpi_device *device, type val) \
 { \
 	int ret = 0; \
 	BUG_ON(device == NULL); \
-	device_lock(&device->dev); \
 	if (!device->driver || !device->driver->ops.hp_ops) \
 		ret = (err); \
 	else if (!device->driver->ops.hp_ops->method) \
 		ret = (def); \
 	else \
 		device->driver->ops.hp_ops->method(device, val); \
-	device_unlock(&device->dev); \
 	return ret; \
 } \
 EXPORT_SYMBOL_GPL(acpihp_dev_##method)
@@ -96,14 +90,12 @@ int acpihp_dev_##method(struct acpi_device *device) \
 { \
 	int ret = 0; \
 	BUG_ON(device == NULL); \
-	device_lock(&device->dev); \
 	if (!device->driver || !device->driver->ops.hp_ops) \
 		ret = (err); \
 	else if (!device->driver->ops.hp_ops->method) \
 		ret = (def); \
 	else \
 		device->driver->ops.hp_ops->method(device);\
-	device_unlock(&device->dev); \
 	return ret; \
 } \
 EXPORT_SYMBOL_GPL(acpihp_dev_##method)
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 18/28] ACPIHP/container: change ACPI container driver to support new hotplug framework
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (16 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 17/28] ACPIHP: block ACPI device driver unloading when executing hotplug operations Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 19/28] ACPIHP/container: move container.c into drivers/acpi/hotplug Jiang Liu
                   ` (10 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

With the new ACPI system device hotplug framework, ACPI device driver
doesn't need to handle hotplug events any more and only need to provide
callbacks for the framework to configure/unconfigure system devices.

So this patch makes following changes to the ACPI container driver:
1) Remove code to handle ACPI hotplug event from the container driver.
2) Add callbacks to configure/unconfigure container device. Actually
   all callbacks are NOOP because ACPI container devices are just
   container to host really ACPI devices.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
---
 drivers/acpi/Kconfig     |    7 +-
 drivers/acpi/container.c |  212 ++++++----------------------------------------
 include/acpi/container.h |   12 ---
 3 files changed, 29 insertions(+), 202 deletions(-)
 delete mode 100644 include/acpi/container.h

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 1f972f0..4615108 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -374,13 +374,14 @@ config ACPI_HOTPLUG_DRIVER
 
 config ACPI_CONTAINER
 	tristate "Container and Module Devices (EXPERIMENTAL)"
-	depends on EXPERIMENTAL
-	default (ACPI_HOTPLUG_MEMORY || ACPI_HOTPLUG_CPU || ACPI_HOTPLUG_IO)
+	depends on ACPI_HOTPLUG
+	default y
 	help
 	  This driver supports ACPI Container and Module devices (IDs
 	  ACPI0004, PNP0A05, and PNP0A06).
 
-	  This helps support hotplug of nodes, CPUs, and memory.
+	  This helps support hotplug of nodes, CPUs, memory and PCI host
+	  bridges.
 
 	  To compile this driver as a module, choose M here:
 	  the module will be called container.
diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c
index 1f9f7d7..fbc4a20 100644
--- a/drivers/acpi/container.c
+++ b/drivers/acpi/container.c
@@ -34,16 +34,13 @@
 #include <linux/acpi.h>
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
-#include <acpi/container.h>
+#include <acpi/acpi_hotplug.h>
 
 #define PREFIX "ACPI: "
 
 #define ACPI_CONTAINER_DEVICE_NAME	"ACPI container device"
 #define ACPI_CONTAINER_CLASS		"container"
 
-#define INSTALL_NOTIFY_HANDLER		1
-#define UNINSTALL_NOTIFY_HANDLER	2
-
 #define _COMPONENT			ACPI_CONTAINER_COMPONENT
 ACPI_MODULE_NAME("container");
 
@@ -54,6 +51,18 @@ MODULE_LICENSE("GPL");
 static int acpi_container_add(struct acpi_device *device);
 static int acpi_container_remove(struct acpi_device *device, int type);
 
+static int acpihp_container_get_devinfo(struct acpi_device *device,
+					struct acpihp_dev_info *info);
+static int acpihp_container_configure(struct acpi_device *device,
+				      struct acpihp_cancel_context *argp);
+static void acpihp_container_unconfigure(struct acpi_device *device);
+
+struct acpihp_dev_ops acpihp_container_ops = {
+	.get_info = acpihp_container_get_devinfo,
+	.configure = acpihp_container_configure,
+	.unconfigure = acpihp_container_unconfigure,
+};
+
 static const struct acpi_device_id container_device_ids[] = {
 	{"ACPI0004", 0},
 	{"PNP0A05", 0},
@@ -69,49 +78,19 @@ static struct acpi_driver acpi_container_driver = {
 	.ops = {
 		.add = acpi_container_add,
 		.remove = acpi_container_remove,
-		},
+		.hp_ops = &acpihp_container_ops,
+	},
 };
 
-/*******************************************************************/
-
-static int is_device_present(acpi_handle handle)
-{
-	acpi_handle temp;
-	acpi_status status;
-	unsigned long long sta;
-
-
-	status = acpi_get_handle(handle, "_STA", &temp);
-	if (ACPI_FAILURE(status))
-		return 1;	/* _STA not found, assume device present */
-
-	status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
-	if (ACPI_FAILURE(status))
-		return 0;	/* Firmware error */
-
-	return ((sta & ACPI_STA_DEVICE_PRESENT) == ACPI_STA_DEVICE_PRESENT);
-}
-
-/*******************************************************************/
 static int acpi_container_add(struct acpi_device *device)
 {
-	struct acpi_container *container;
-
-
 	if (!device) {
 		printk(KERN_ERR PREFIX "device is NULL\n");
 		return -EINVAL;
 	}
 
-	container = kzalloc(sizeof(struct acpi_container), GFP_KERNEL);
-	if (!container)
-		return -ENOMEM;
-
-	container->handle = device->handle;
 	strcpy(acpi_device_name(device), ACPI_CONTAINER_DEVICE_NAME);
 	strcpy(acpi_device_class(device), ACPI_CONTAINER_CLASS);
-	device->driver_data = container;
-
 	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device <%s> bid <%s>\n",
 			  acpi_device_name(device), acpi_device_bid(device)));
 
@@ -120,176 +99,35 @@ static int acpi_container_add(struct acpi_device *device)
 
 static int acpi_container_remove(struct acpi_device *device, int type)
 {
-	acpi_status status = AE_OK;
-	struct acpi_container *pc = NULL;
-
-	pc = acpi_driver_data(device);
-	kfree(pc);
-	return status;
+	return AE_OK;
 }
 
-static int container_device_add(struct acpi_device **device, acpi_handle handle)
+static int acpihp_container_get_devinfo(struct acpi_device *device,
+					struct acpihp_dev_info *info)
 {
-	acpi_handle phandle;
-	struct acpi_device *pdev;
-	int result;
-
-
-	if (acpi_get_parent(handle, &phandle)) {
-		return -ENODEV;
-	}
-
-	if (acpi_bus_get_device(phandle, &pdev)) {
-		return -ENODEV;
-	}
-
-	if (acpi_bus_add(device, pdev, handle, ACPI_BUS_TYPE_DEVICE)) {
-		return -ENODEV;
-	}
-
-	result = acpi_bus_start(*device);
+	info->type = ACPIHP_DEV_TYPE_CONTAINER;
 
-	return result;
+	return 0;
 }
 
-static void container_notify_cb(acpi_handle handle, u32 type, void *context)
+static int acpihp_container_configure(struct acpi_device *device,
+				      struct acpihp_cancel_context *argp)
 {
-	struct acpi_device *device = NULL;
-	int result;
-	int present;
-	acpi_status status;
-	u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */
-
-	switch (type) {
-	case ACPI_NOTIFY_BUS_CHECK:
-		/* Fall through */
-	case ACPI_NOTIFY_DEVICE_CHECK:
-		printk(KERN_WARNING "Container driver received %s event\n",
-		       (type == ACPI_NOTIFY_BUS_CHECK) ?
-		       "ACPI_NOTIFY_BUS_CHECK" : "ACPI_NOTIFY_DEVICE_CHECK");
-
-		present = is_device_present(handle);
-		status = acpi_bus_get_device(handle, &device);
-		if (!present) {
-			if (ACPI_SUCCESS(status)) {
-				/* device exist and this is a remove request */
-				device->flags.eject_pending = 1;
-				kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE);
-				return;
-			}
-			break;
-		}
-
-		if (!ACPI_FAILURE(status) || device)
-			break;
-
-		result = container_device_add(&device, handle);
-		if (result) {
-			printk(KERN_WARNING "Failed to add container\n");
-			break;
-		}
-
-		kobject_uevent(&device->dev.kobj, KOBJ_ONLINE);
-		ost_code = ACPI_OST_SC_SUCCESS;
-		break;
-
-	case ACPI_NOTIFY_EJECT_REQUEST:
-		if (!acpi_bus_get_device(handle, &device) && device) {
-			device->flags.eject_pending = 1;
-			kobject_uevent(&device->dev.kobj, KOBJ_OFFLINE);
-			return;
-		}
-		break;
-
-	default:
-		/* non-hotplug event; possibly handled by other handler */
-		return;
-	}
-
-	/* Inform firmware that the hotplug operation has completed */
-	(void) acpi_evaluate_hotplug_ost(handle, type, ost_code, NULL);
-	return;
+	return 0;
 }
 
-static acpi_status
-container_walk_namespace_cb(acpi_handle handle,
-			    u32 lvl, void *context, void **rv)
+static void acpihp_container_unconfigure(struct acpi_device *device)
 {
-	char *hid = NULL;
-	struct acpi_device_info *info;
-	acpi_status status;
-	int *action = context;
-
-	status = acpi_get_object_info(handle, &info);
-	if (ACPI_FAILURE(status)) {
-		return AE_OK;
-	}
-
-	if (info->valid & ACPI_VALID_HID)
-		hid = info->hardware_id.string;
-
-	if (hid == NULL) {
-		goto end;
-	}
-
-	if (strcmp(hid, "ACPI0004") && strcmp(hid, "PNP0A05") &&
-	    strcmp(hid, "PNP0A06")) {
-		goto end;
-	}
-
-	switch (*action) {
-	case INSTALL_NOTIFY_HANDLER:
-		acpi_install_notify_handler(handle,
-					    ACPI_SYSTEM_NOTIFY,
-					    container_notify_cb, NULL);
-		break;
-	case UNINSTALL_NOTIFY_HANDLER:
-		acpi_remove_notify_handler(handle,
-					   ACPI_SYSTEM_NOTIFY,
-					   container_notify_cb);
-		break;
-	default:
-		break;
-	}
-
-      end:
-	kfree(info);
-
-	return AE_OK;
 }
 
 static int __init acpi_container_init(void)
 {
-	int result = 0;
-	int action = INSTALL_NOTIFY_HANDLER;
-
-	result = acpi_bus_register_driver(&acpi_container_driver);
-	if (result < 0) {
-		return (result);
-	}
-
-	/* register notify handler to every container device */
-	acpi_walk_namespace(ACPI_TYPE_DEVICE,
-			    ACPI_ROOT_OBJECT,
-			    ACPI_UINT32_MAX,
-			    container_walk_namespace_cb, NULL, &action, NULL);
-
-	return (0);
+	return acpi_bus_register_driver(&acpi_container_driver);
 }
 
 static void __exit acpi_container_exit(void)
 {
-	int action = UNINSTALL_NOTIFY_HANDLER;
-
-
-	acpi_walk_namespace(ACPI_TYPE_DEVICE,
-			    ACPI_ROOT_OBJECT,
-			    ACPI_UINT32_MAX,
-			    container_walk_namespace_cb, NULL, &action, NULL);
-
 	acpi_bus_unregister_driver(&acpi_container_driver);
-
-	return;
 }
 
 module_init(acpi_container_init);
diff --git a/include/acpi/container.h b/include/acpi/container.h
deleted file mode 100644
index a703f14..0000000
--- a/include/acpi/container.h
+++ /dev/null
@@ -1,12 +0,0 @@
-#ifndef __ACPI_CONTAINER_H
-#define __ACPI_CONTAINER_H
-
-#include <linux/kernel.h>
-
-struct acpi_container {
-	acpi_handle handle;
-	unsigned long sun;
-	int state;
-};
-
-#endif				/* __ACPI_CONTAINER_H */
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 19/28] ACPIHP/container: move container.c into drivers/acpi/hotplug
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (17 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 18/28] ACPIHP/container: change ACPI container driver to support new hotplug framework Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 20/28] ACPI/processor: remove dead code from processor_driver.c Jiang Liu
                   ` (9 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

Now the ACPI container driver is only used to support the ACPI system
device hotplug framework, so move it into drivers/acpi/hotplug.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
---
 drivers/acpi/Makefile            |    1 -
 drivers/acpi/container.c         |  134 -------------------------------------
 drivers/acpi/hotplug/Makefile    |    2 +
 drivers/acpi/hotplug/container.c |  135 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 137 insertions(+), 135 deletions(-)
 delete mode 100644 drivers/acpi/container.c
 create mode 100644 drivers/acpi/hotplug/container.c

diff --git a/drivers/acpi/Makefile b/drivers/acpi/Makefile
index 17bea6c..fa5b6d3 100644
--- a/drivers/acpi/Makefile
+++ b/drivers/acpi/Makefile
@@ -53,7 +53,6 @@ obj-$(CONFIG_ACPI_FAN)		+= fan.o
 obj-$(CONFIG_ACPI_VIDEO)	+= video.o
 obj-$(CONFIG_ACPI_PCI_SLOT)	+= pci_slot.o
 obj-$(CONFIG_ACPI_PROCESSOR)	+= processor.o
-obj-$(CONFIG_ACPI_CONTAINER)	+= container.o
 obj-$(CONFIG_ACPI_THERMAL)	+= thermal.o
 obj-$(CONFIG_ACPI_HOTPLUG_MEMORY) += acpi_memhotplug.o
 obj-$(CONFIG_ACPI_BATTERY)	+= battery.o
diff --git a/drivers/acpi/container.c b/drivers/acpi/container.c
deleted file mode 100644
index fbc4a20..0000000
--- a/drivers/acpi/container.c
+++ /dev/null
@@ -1,134 +0,0 @@
-/*
- * acpi_container.c  - ACPI Generic Container Driver
- * ($Revision: )
- *
- * Copyright (C) 2004 Anil S Keshavamurthy (anil.s.keshavamurthy@intel.com)
- * Copyright (C) 2004 Keiichiro Tokunaga (tokunaga.keiich@jp.fujitsu.com)
- * Copyright (C) 2004 Motoyuki Ito (motoyuki@soft.fujitsu.com)
- * Copyright (C) 2004 Intel Corp.
- * Copyright (C) 2004 FUJITSU LIMITED
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- *
- *  This program is free software; you can redistribute it and/or modify
- *  it under the terms of the GNU General Public License as published by
- *  the Free Software Foundation; either version 2 of the License, or (at
- *  your option) any later version.
- *
- *  This program is distributed in the hope that it will be useful, but
- *  WITHOUT ANY WARRANTY; without even the implied warranty of
- *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- *  General Public License for more details.
- *
- *  You should have received a copy of the GNU General Public License along
- *  with this program; if not, write to the Free Software Foundation, Inc.,
- *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
- *
- * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
- */
-#include <linux/kernel.h>
-#include <linux/module.h>
-#include <linux/init.h>
-#include <linux/slab.h>
-#include <linux/types.h>
-#include <linux/acpi.h>
-#include <acpi/acpi_bus.h>
-#include <acpi/acpi_drivers.h>
-#include <acpi/acpi_hotplug.h>
-
-#define PREFIX "ACPI: "
-
-#define ACPI_CONTAINER_DEVICE_NAME	"ACPI container device"
-#define ACPI_CONTAINER_CLASS		"container"
-
-#define _COMPONENT			ACPI_CONTAINER_COMPONENT
-ACPI_MODULE_NAME("container");
-
-MODULE_AUTHOR("Anil S Keshavamurthy");
-MODULE_DESCRIPTION("ACPI container driver");
-MODULE_LICENSE("GPL");
-
-static int acpi_container_add(struct acpi_device *device);
-static int acpi_container_remove(struct acpi_device *device, int type);
-
-static int acpihp_container_get_devinfo(struct acpi_device *device,
-					struct acpihp_dev_info *info);
-static int acpihp_container_configure(struct acpi_device *device,
-				      struct acpihp_cancel_context *argp);
-static void acpihp_container_unconfigure(struct acpi_device *device);
-
-struct acpihp_dev_ops acpihp_container_ops = {
-	.get_info = acpihp_container_get_devinfo,
-	.configure = acpihp_container_configure,
-	.unconfigure = acpihp_container_unconfigure,
-};
-
-static const struct acpi_device_id container_device_ids[] = {
-	{"ACPI0004", 0},
-	{"PNP0A05", 0},
-	{"PNP0A06", 0},
-	{"", 0},
-};
-MODULE_DEVICE_TABLE(acpi, container_device_ids);
-
-static struct acpi_driver acpi_container_driver = {
-	.name = "container",
-	.class = ACPI_CONTAINER_CLASS,
-	.ids = container_device_ids,
-	.ops = {
-		.add = acpi_container_add,
-		.remove = acpi_container_remove,
-		.hp_ops = &acpihp_container_ops,
-	},
-};
-
-static int acpi_container_add(struct acpi_device *device)
-{
-	if (!device) {
-		printk(KERN_ERR PREFIX "device is NULL\n");
-		return -EINVAL;
-	}
-
-	strcpy(acpi_device_name(device), ACPI_CONTAINER_DEVICE_NAME);
-	strcpy(acpi_device_class(device), ACPI_CONTAINER_CLASS);
-	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device <%s> bid <%s>\n",
-			  acpi_device_name(device), acpi_device_bid(device)));
-
-	return 0;
-}
-
-static int acpi_container_remove(struct acpi_device *device, int type)
-{
-	return AE_OK;
-}
-
-static int acpihp_container_get_devinfo(struct acpi_device *device,
-					struct acpihp_dev_info *info)
-{
-	info->type = ACPIHP_DEV_TYPE_CONTAINER;
-
-	return 0;
-}
-
-static int acpihp_container_configure(struct acpi_device *device,
-				      struct acpihp_cancel_context *argp)
-{
-	return 0;
-}
-
-static void acpihp_container_unconfigure(struct acpi_device *device)
-{
-}
-
-static int __init acpi_container_init(void)
-{
-	return acpi_bus_register_driver(&acpi_container_driver);
-}
-
-static void __exit acpi_container_exit(void)
-{
-	acpi_bus_unregister_driver(&acpi_container_driver);
-}
-
-module_init(acpi_container_init);
-module_exit(acpi_container_exit);
diff --git a/drivers/acpi/hotplug/Makefile b/drivers/acpi/hotplug/Makefile
index 57e348a..4b62f7e 100644
--- a/drivers/acpi/hotplug/Makefile
+++ b/drivers/acpi/hotplug/Makefile
@@ -17,3 +17,5 @@ acpihp_drv-y					+= cancel.o
 acpihp_drv-y					+= configure.o
 acpihp_drv-y					+= state_machine.o
 acpihp_drv-y					+= sysfs.o
+
+obj-$(CONFIG_ACPI_CONTAINER)	+= container.o
diff --git a/drivers/acpi/hotplug/container.c b/drivers/acpi/hotplug/container.c
new file mode 100644
index 0000000..31dc9f8
--- /dev/null
+++ b/drivers/acpi/hotplug/container.c
@@ -0,0 +1,135 @@
+/*
+ * acpi_container.c  - ACPI Generic Container Driver
+ * ($Revision: )
+ *
+ * Copyright (C) 2004 Anil S Keshavamurthy (anil.s.keshavamurthy@intel.com)
+ * Copyright (C) 2004 Keiichiro Tokunaga (tokunaga.keiich@jp.fujitsu.com)
+ * Copyright (C) 2004 Motoyuki Ito (motoyuki@soft.fujitsu.com)
+ * Copyright (C) 2004 Intel Corp.
+ * Copyright (C) 2004 FUJITSU LIMITED
+ * Copyright (C) 2004 Jiang Liu (jiang.liu@huawei.com)
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; either version 2 of the License, or (at
+ *  your option) any later version.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ *  You should have received a copy of the GNU General Public License along
+ *  with this program; if not, write to the Free Software Foundation, Inc.,
+ *  59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/acpi.h>
+#include <acpi/acpi_bus.h>
+#include <acpi/acpi_drivers.h>
+#include <acpi/acpi_hotplug.h>
+
+#define PREFIX "ACPI: "
+
+#define ACPI_CONTAINER_DEVICE_NAME	"ACPI container device"
+#define ACPI_CONTAINER_CLASS		"container"
+
+#define _COMPONENT			ACPI_CONTAINER_COMPONENT
+ACPI_MODULE_NAME("container");
+
+MODULE_AUTHOR("Anil S Keshavamurthy");
+MODULE_DESCRIPTION("ACPI container driver");
+MODULE_LICENSE("GPL");
+
+static int acpi_container_add(struct acpi_device *device);
+static int acpi_container_remove(struct acpi_device *device, int type);
+
+static int acpihp_container_get_devinfo(struct acpi_device *device,
+					struct acpihp_dev_info *info);
+static int acpihp_container_configure(struct acpi_device *device,
+				      struct acpihp_cancel_context *argp);
+static void acpihp_container_unconfigure(struct acpi_device *device);
+
+struct acpihp_dev_ops acpihp_container_ops = {
+	.get_info = acpihp_container_get_devinfo,
+	.configure = acpihp_container_configure,
+	.unconfigure = acpihp_container_unconfigure,
+};
+
+static const struct acpi_device_id container_device_ids[] = {
+	{"ACPI0004", 0},
+	{"PNP0A05", 0},
+	{"PNP0A06", 0},
+	{"", 0},
+};
+MODULE_DEVICE_TABLE(acpi, container_device_ids);
+
+static struct acpi_driver acpi_container_driver = {
+	.name = "container",
+	.class = ACPI_CONTAINER_CLASS,
+	.ids = container_device_ids,
+	.ops = {
+		.add = acpi_container_add,
+		.remove = acpi_container_remove,
+		.hp_ops = &acpihp_container_ops,
+	},
+};
+
+static int acpi_container_add(struct acpi_device *device)
+{
+	if (!device) {
+		printk(KERN_ERR PREFIX "device is NULL\n");
+		return -EINVAL;
+	}
+
+	strcpy(acpi_device_name(device), ACPI_CONTAINER_DEVICE_NAME);
+	strcpy(acpi_device_class(device), ACPI_CONTAINER_CLASS);
+	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Device <%s> bid <%s>\n",
+			  acpi_device_name(device), acpi_device_bid(device)));
+
+	return 0;
+}
+
+static int acpi_container_remove(struct acpi_device *device, int type)
+{
+	return AE_OK;
+}
+
+static int acpihp_container_get_devinfo(struct acpi_device *device,
+					struct acpihp_dev_info *info)
+{
+	info->type = ACPIHP_DEV_TYPE_CONTAINER;
+
+	return 0;
+}
+
+static int acpihp_container_configure(struct acpi_device *device,
+				      struct acpihp_cancel_context *argp)
+{
+	return 0;
+}
+
+static void acpihp_container_unconfigure(struct acpi_device *device)
+{
+}
+
+static int __init acpi_container_init(void)
+{
+	return acpi_bus_register_driver(&acpi_container_driver);
+}
+
+static void __exit acpi_container_exit(void)
+{
+	acpi_bus_unregister_driver(&acpi_container_driver);
+}
+
+module_init(acpi_container_init);
+module_exit(acpi_container_exit);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 20/28] ACPI/processor: remove dead code from processor_driver.c
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (18 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 19/28] ACPIHP/container: move container.c into drivers/acpi/hotplug Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 21/28] ACPIHP/processor: reorganize ACPI processor driver for new hotplug framework Jiang Liu
                   ` (8 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

There are some dead code in processor_driver.c, so clean it up.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
---
 drivers/acpi/processor_driver.c |    9 ---------
 1 file changed, 9 deletions(-)

diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index bfc31cb..abbce9e 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -61,17 +61,11 @@
 
 #define ACPI_PROCESSOR_CLASS		"processor"
 #define ACPI_PROCESSOR_DEVICE_NAME	"Processor"
-#define ACPI_PROCESSOR_FILE_INFO	"info"
-#define ACPI_PROCESSOR_FILE_THROTTLING	"throttling"
-#define ACPI_PROCESSOR_FILE_LIMIT	"limit"
 #define ACPI_PROCESSOR_NOTIFY_PERFORMANCE 0x80
 #define ACPI_PROCESSOR_NOTIFY_POWER	0x81
 #define ACPI_PROCESSOR_NOTIFY_THROTTLING	0x82
 #define ACPI_PROCESSOR_DEVICE_HID	"ACPI0007"
 
-#define ACPI_PROCESSOR_LIMIT_USER	0
-#define ACPI_PROCESSOR_LIMIT_THERMAL	1
-
 #define _COMPONENT		ACPI_PROCESSOR_COMPONENT
 ACPI_MODULE_NAME("processor_driver");
 
@@ -261,9 +255,6 @@ static int acpi_processor_get_info(struct acpi_device *device)
 	if (!pr)
 		return -EINVAL;
 
-	if (num_online_cpus() > 1)
-		errata.smp = TRUE;
-
 	acpi_processor_errata(pr);
 
 	/*
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 21/28] ACPIHP/processor: reorganize ACPI processor driver for new hotplug framework
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (19 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 20/28] ACPI/processor: remove dead code from processor_driver.c Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 22/28] ACPIHP/processor: protect accesses to device->driver_data Jiang Liu
                   ` (7 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

This patch reorganizes code in processor_driver.c to parepare for
integration with the new hotplug framework. Common code could be
shared among acpi_processor_add(), acpi_processor_remove() and hotplug
has been reorganized as:
acpi_processor_start()
acpi_processor_stop()
acpi_processor_link()
acpi_processor_unlink()

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
---
 drivers/acpi/processor_driver.c |  179 +++++++++++++++++++++++----------------
 include/acpi/processor.h        |    2 +
 2 files changed, 108 insertions(+), 73 deletions(-)

diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index abbce9e..4f2fec0 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -105,6 +105,8 @@ static struct acpi_driver acpi_processor_driver = {
 #define INSTALL_NOTIFY_HANDLER		1
 #define UNINSTALL_NOTIFY_HANDLER	2
 
+static DEFINE_PER_CPU(void *, processor_device_array);
+
 DEFINE_PER_CPU(struct acpi_processor *, processors);
 EXPORT_PER_CPU_SYMBOL(processors);
 
@@ -327,11 +329,11 @@ static int acpi_processor_get_info(struct acpi_device *device)
 	 * \_SB.SCK1.CPU0
 	 * Rename the processor device bus id. And the new bus id will be
 	 * generated as the following format:
-	 * CPU+CPU ID.
+	 * CPU+ACPI ID.
 	 */
-	sprintf(acpi_device_bid(device), "CPU%X", pr->id);
-	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Processor [%d:%d]\n", pr->id,
-			  pr->acpi_id));
+	sprintf(acpi_device_bid(device), "CPU%X", pr->acpi_id);
+	ACPI_DEBUG_PRINT((ACPI_DB_INFO, "Processor [%d:%d]\n", pr->acpi_id,
+			  pr->id));
 
 	if (!object.processor.pblk_address)
 		ACPI_DEBUG_PRINT((ACPI_DB_INFO, "No PBLK (NULL address)\n"));
@@ -355,19 +357,67 @@ static int acpi_processor_get_info(struct acpi_device *device)
 		request_region(pr->throttling.address, 6, "ACPI CPU throttle");
 	}
 
+	return 0;
+}
+
+static int acpi_processor_link(struct acpi_device *device,
+			       struct acpi_processor *pr)
+{
+	struct device *dev;
+	acpi_status status;
+	unsigned long long value;
+
+	if (pr->flags.device_linked)
+		return 0;
+
 	/*
 	 * If ACPI describes a slot number for this CPU, we can use it
 	 * ensure we get the right value in the "physical id" field
 	 * of /proc/cpuinfo
 	 */
-	status = acpi_evaluate_object(pr->handle, "_SUN", NULL, &buffer);
+	status = acpi_evaluate_integer(pr->handle, "_SUN", NULL, &value);
 	if (ACPI_SUCCESS(status))
-		arch_fix_phys_package_id(pr->id, object.integer.value);
+		arch_fix_phys_package_id(pr->id, value);
+
+	/*
+	 * Buggy BIOS check
+	 * ACPI id of processors can be reported wrongly by the BIOS.
+	 * Don't trust it blindly
+	 */
+	if (per_cpu(processor_device_array, pr->id) != NULL &&
+	    per_cpu(processor_device_array, pr->id) != device) {
+		dev_warn(&device->dev,
+			 "BIOS reported wrong ACPI id for the processor\n");
+		return -ENODEV;
+	}
+	per_cpu(processor_device_array, pr->id) = device;
+	per_cpu(processors, pr->id) = pr;
+
+	dev = get_cpu_device(pr->id);
+	if (sysfs_create_link(&device->dev.kobj, &dev->kobj, "sysdev")) {
+		/*
+		 * processor_device_array is not cleared to allow checks
+		 * for buggy BIOS
+		 */ 
+		per_cpu(processors, pr->id) = NULL;
+		return -EFAULT;
+	}
+
+	pr->flags.device_linked = 1;
 
 	return 0;
 }
 
-static DEFINE_PER_CPU(void *, processor_device_array);
+static void acpi_processor_unlink(struct acpi_device *device,
+				  struct acpi_processor *pr)
+{
+	if (pr->flags.device_linked) {
+		sysfs_remove_link(&device->dev.kobj, "sysdev");
+		per_cpu(processor_device_array, pr->id) = NULL;
+		per_cpu(processors, pr->id) = NULL;
+		pr->flags.device_linked = 0;
+	}
+}
 
 static void acpi_processor_notify(struct acpi_device *device, u32 event)
 {
@@ -422,11 +472,11 @@ static int acpi_cpu_soft_notify(struct notifier_block *nfb,
 		if (pr->flags.need_hotplug_init) {
 			printk(KERN_INFO "Will online and init hotplugged "
 			       "CPU: %d\n", pr->id);
-			WARN(acpi_processor_start(pr), "Failed to start CPU:"
-				" %d\n", pr->id);
+			WARN(acpi_processor_start(pr),
+				"Failed to start CPU: %d\n", pr->id);
 			pr->flags.need_hotplug_init = 0;
 		/* Normal CPU soft online event */
-		} else {
+		} else if (pr->flags.device_started) {
 			acpi_processor_ppc_has_changed(pr, 0);
 			acpi_processor_hotplug(pr);
 			acpi_processor_reevaluate_tstate(pr, action);
@@ -458,6 +508,9 @@ static __ref int acpi_processor_start(struct acpi_processor *pr)
 	struct acpi_device *device = per_cpu(processor_device_array, pr->id);
 	int result = 0;
 
+	if (pr->flags.device_started)
+		return 0;
+
 #ifdef CONFIG_CPU_FREQ
 	acpi_processor_ppc_has_changed(pr, 0);
 	acpi_processor_load_module(pr);
@@ -493,6 +546,8 @@ static __ref int acpi_processor_start(struct acpi_processor *pr)
 		goto err_remove_sysfs_thermal;
 	}
 
+	pr->flags.device_started = 1;
+
 	return 0;
 
 err_remove_sysfs_thermal:
@@ -505,6 +560,21 @@ err_power_exit:
 	return result;
 }
 
+static void acpi_processor_stop(struct acpi_device *device,
+				struct acpi_processor *pr)
+{
+	if (pr->flags.device_started) {
+		acpi_processor_power_exit(pr, device);
+		if (pr->cdev) {
+			sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
+			sysfs_remove_link(&pr->cdev->device.kobj, "device");
+			thermal_cooling_device_unregister(pr->cdev);
+			pr->cdev = NULL;
+		}
+		pr->flags.device_started = 0;
+	}
+}
+
 /*
  * Do not put anything in here which needs the core to be online.
  * For example MSR access or setting up things which check for cpuinfo_x86
@@ -513,9 +583,8 @@ err_power_exit:
  */
 static int __cpuinit acpi_processor_add(struct acpi_device *device)
 {
-	struct acpi_processor *pr = NULL;
-	int result = 0;
-	struct device *dev;
+	struct acpi_processor *pr;
+	int result;
 
 	pr = kzalloc(sizeof(struct acpi_processor), GFP_KERNEL);
 	if (!pr)
@@ -532,60 +601,36 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device)
 	device->driver_data = pr;
 
 	result = acpi_processor_get_info(device);
-	if (result) {
-		/* Processor is physically not present */
-		return 0;
-	}
-
-#ifdef CONFIG_SMP
-	if (pr->id >= setup_max_cpus && pr->id != 0)
-		return 0;
-#endif
-
-	BUG_ON((pr->id >= nr_cpu_ids) || (pr->id < 0));
-
-	/*
-	 * Buggy BIOS check
-	 * ACPI id of processors can be reported wrongly by the BIOS.
-	 * Don't trust it blindly
-	 */
-	if (per_cpu(processor_device_array, pr->id) != NULL &&
-	    per_cpu(processor_device_array, pr->id) != device) {
-		printk(KERN_WARNING "BIOS reported wrong ACPI id "
-			"for the processor\n");
-		result = -ENODEV;
+	if (result)
 		goto err_free_cpumask;
-	}
-	per_cpu(processor_device_array, pr->id) = device;
-
-	per_cpu(processors, pr->id) = pr;
-
-	dev = get_cpu_device(pr->id);
-	if (sysfs_create_link(&device->dev.kobj, &dev->kobj, "sysdev")) {
-		result = -EFAULT;
-		goto err_clear_processor;
-	}
 
 	/*
-	 * Do not start hotplugged CPUs now, but when they
-	 * are onlined the first time
+	 * Delay linking with logical CPU device if:
+	 * 1) no CPUID assigned yet
+	 * 2) processor won't be boot if CPUID is bigger than setup_max_cpus
+	 * They will be handled by CPU hotplug logical later.
 	 */
-	if (pr->flags.need_hotplug_init)
+	if (pr->id == -1)
 		return 0;
+	if (IS_BUILTIN(CONFIG_SMP) && pr->id >= setup_max_cpus && pr->id != 0)
+		return 0;
+	BUG_ON((pr->id >= nr_cpu_ids) || (pr->id < 0));
 
-	result = acpi_processor_start(pr);
+	result = acpi_processor_link(device, pr);
 	if (result)
-		goto err_remove_sysfs;
+		goto err_unlock;
+	if (cpu_online(pr->id)) {
+		result = acpi_processor_start(pr);
+		if (result)
+			goto err_unlink;
+	}
 
 	return 0;
 
-err_remove_sysfs:
-	sysfs_remove_link(&device->dev.kobj, "sysdev");
-err_clear_processor:
-	/*
-	 * processor_device_array is not cleared to allow checks for buggy BIOS
-	 */ 
-	per_cpu(processors, pr->id) = NULL;
+err_unlink:
+	acpi_processor_unlink(device, pr);
+err_unlock:
+	put_online_cpus();
 err_free_cpumask:
 	free_cpumask_var(pr->throttling.shared_cpu_map);
 err_free_pr:
@@ -595,14 +640,12 @@ err_free_pr:
 
 static int acpi_processor_remove(struct acpi_device *device, int type)
 {
-	struct acpi_processor *pr = NULL;
-
+	struct acpi_processor *pr;
 
 	if (!device || !acpi_driver_data(device))
 		return -EINVAL;
 
 	pr = acpi_driver_data(device);
-
 	if (pr->id >= nr_cpu_ids)
 		goto free;
 
@@ -611,21 +654,11 @@ static int acpi_processor_remove(struct acpi_device *device, int type)
 			return -EINVAL;
 	}
 
-	acpi_processor_power_exit(pr, device);
-
-	sysfs_remove_link(&device->dev.kobj, "sysdev");
-
-	if (pr->cdev) {
-		sysfs_remove_link(&device->dev.kobj, "thermal_cooling");
-		sysfs_remove_link(&pr->cdev->device.kobj, "device");
-		thermal_cooling_device_unregister(pr->cdev);
-		pr->cdev = NULL;
-	}
-
-	per_cpu(processors, pr->id) = NULL;
-	per_cpu(processor_device_array, pr->id) = NULL;
+	acpi_processor_stop(device, pr);
+	acpi_processor_unlink(device, pr);
 
 free:
+	device->driver_data = NULL;
 	free_cpumask_var(pr->throttling.shared_cpu_map);
 	kfree(pr);
 
diff --git a/include/acpi/processor.h b/include/acpi/processor.h
index 64ec644..ae7d94e 100644
--- a/include/acpi/processor.h
+++ b/include/acpi/processor.h
@@ -193,6 +193,8 @@ struct acpi_processor_flags {
 	u8 power_setup_done:1;
 	u8 bm_rld_set:1;
 	u8 need_hotplug_init:1;
+	u8 device_linked:1;
+	u8 device_started:1;
 };
 
 struct acpi_processor {
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 22/28] ACPIHP/processor: protect accesses to device->driver_data
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (20 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 21/28] ACPIHP/processor: reorganize ACPI processor driver for new hotplug framework Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 23/28] ACPIHP/processor: enhance processor driver to support new hotplug framework Jiang Liu
                   ` (6 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

CPU hotplug notification handler acpi_cpu_soft_notify() and driver
unbind method acpi_processor_remove() may be concurrently called.
acpi_cpu_soft_notify() will access device->driver_data, but that
data structure may be destroyed by acpi_processor_remove().

On the other hand, acpi_cpu_soft_notify() is always called under
protection of get_online_cpus(), so use get_online_cpus() to serialize
all accesses and modifications to device->driver_data.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
---
 drivers/acpi/processor_driver.c |    7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index 4f2fec0..110c5ac 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -616,6 +616,8 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device)
 		return 0;
 	BUG_ON((pr->id >= nr_cpu_ids) || (pr->id < 0));
 
+	/* block CPU online/offline operations */
+	get_online_cpus();
 	result = acpi_processor_link(device, pr);
 	if (result)
 		goto err_unlock;
@@ -624,6 +626,7 @@ static int __cpuinit acpi_processor_add(struct acpi_device *device)
 		if (result)
 			goto err_unlink;
 	}
+	put_online_cpus();
 
 	return 0;
 
@@ -654,8 +657,10 @@ static int acpi_processor_remove(struct acpi_device *device, int type)
 			return -EINVAL;
 	}
 
-	acpi_processor_stop(device, pr);
+	get_online_cpus();
+	acpi_processor_stop(pr);
 	acpi_processor_unlink(device, pr);
+	put_online_cpus();
 
 free:
 	device->driver_data = NULL;
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 23/28] ACPIHP/processor: enhance processor driver to support new hotplug framework
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (21 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 22/28] ACPIHP/processor: protect accesses to device->driver_data Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 24/28] CPU: introduce busy flag to temporarily disable CPU online sysfs interface Jiang Liu
                   ` (5 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

Refine ACPI processor driver to support new ACPI system device hotplug
framework with following changes:
1) Remove code to handle ACPI hotplug events from ACPI processor
   driver, now hotplug events will be handled by the framework.
2) Provides callbacks for the framework to add CPU into or remove CPU
   from running system.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
Signed-off-by: Hanjun Guo <guohanjun@huawei.com>
---
 drivers/acpi/Kconfig            |   11 +-
 drivers/acpi/processor_driver.c |  345 +++++++++++++--------------------------
 2 files changed, 121 insertions(+), 235 deletions(-)

diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
index 4615108..490b9a4 100644
--- a/drivers/acpi/Kconfig
+++ b/drivers/acpi/Kconfig
@@ -206,12 +206,6 @@ config ACPI_IPMI
 	  To compile this driver as a module, choose M here:
 	  the module will be called as acpi_ipmi.
 
-config ACPI_HOTPLUG_CPU
-	bool
-	depends on EXPERIMENTAL && ACPI_PROCESSOR && HOTPLUG_CPU
-	select ACPI_CONTAINER
-	default y
-
 config ACPI_PROCESSOR_AGGREGATOR
 	tristate "Processor Aggregator"
 	depends on ACPI_PROCESSOR
@@ -372,6 +366,11 @@ config ACPI_HOTPLUG_DRIVER
 	  To compile this driver as a module, choose M here:
 	  the module will be called acpihp_drv.
 
+config ACPI_HOTPLUG_CPU
+	bool
+	depends on ACPI_HOTPLUG && ACPI_PROCESSOR && HOTPLUG_CPU
+	default y
+
 config ACPI_CONTAINER
 	tristate "Container and Module Devices (EXPERIMENTAL)"
 	depends on ACPI_HOTPLUG
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index 110c5ac..25079ca 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -56,6 +56,7 @@
 #include <acpi/acpi_bus.h>
 #include <acpi/acpi_drivers.h>
 #include <acpi/processor.h>
+#include <acpi/acpi_hotplug.h>
 
 #define PREFIX "ACPI: "
 
@@ -76,9 +77,10 @@ MODULE_LICENSE("GPL");
 static int acpi_processor_add(struct acpi_device *device);
 static int acpi_processor_remove(struct acpi_device *device, int type);
 static void acpi_processor_notify(struct acpi_device *device, u32 event);
-static acpi_status acpi_processor_hotadd_init(struct acpi_processor *pr);
-static int acpi_processor_handle_eject(struct acpi_processor *pr);
 static int acpi_processor_start(struct acpi_processor *pr);
+#ifdef	CONFIG_ACPI_HOTPLUG_CPU
+static struct acpihp_dev_ops acpi_processor_hp_ops;
+#endif
 
 static const struct acpi_device_id processor_device_ids[] = {
 	{ACPI_PROCESSOR_OBJECT_HID, 0},
@@ -98,13 +100,13 @@ static struct acpi_driver acpi_processor_driver = {
 		.add = acpi_processor_add,
 		.remove = acpi_processor_remove,
 		.notify = acpi_processor_notify,
-		},
+#ifdef CONFIG_ACPI_HOTPLUG_CPU
+		.hp_ops = &acpi_processor_hp_ops,
+#endif
+	},
 	.drv.pm = &acpi_processor_pm,
 };
 
-#define INSTALL_NOTIFY_HANDLER		1
-#define UNINSTALL_NOTIFY_HANDLER	2
-
 static DEFINE_PER_CPU(void *, processor_device_array);
 
 DEFINE_PER_CPU(struct acpi_processor *, processors);
@@ -314,15 +316,6 @@ static int acpi_processor_get_info(struct acpi_device *device)
 	pr->id = cpu_index;
 
 	/*
-	 *  Extra Processor objects may be enumerated on MP systems with
-	 *  less than the max # of CPUs. They should be ignored _iff
-	 *  they are physically not present.
-	 */
-	if (pr->id == -1) {
-		if (ACPI_FAILURE(acpi_processor_hotadd_init(pr)))
-			return -ENODEV;
-	}
-	/*
 	 * On some boxes several processors use the same processor bus id.
 	 * But they are located in different scope. For example:
 	 * \_SB.SCK0.CPU0
@@ -649,20 +642,12 @@ static int acpi_processor_remove(struct acpi_device *device, int type)
 		return -EINVAL;
 
 	pr = acpi_driver_data(device);
-	if (pr->id >= nr_cpu_ids)
-		goto free;
-
-	if (type == ACPI_BUS_REMOVAL_EJECT) {
-		if (acpi_processor_handle_eject(pr))
-			return -EINVAL;
-	}
 
 	get_online_cpus();
-	acpi_processor_stop(pr);
+	acpi_processor_stop(device, pr);
 	acpi_processor_unlink(device, pr);
 	put_online_cpus();
 
-free:
 	device->driver_data = NULL;
 	free_cpumask_var(pr->throttling.shared_cpu_map);
 	kfree(pr);
@@ -675,258 +660,160 @@ free:
  * 	Acpi processor hotplug support 				       	    *
  ****************************************************************************/
 
-static int is_processor_present(acpi_handle handle)
+static void acpi_processor_reset(struct acpi_device *device, struct acpi_processor *pr)
 {
-	acpi_status status;
-	unsigned long long sta = 0;
-
-
-	status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
-
-	if (ACPI_SUCCESS(status) && (sta & ACPI_STA_DEVICE_PRESENT))
-		return 1;
-
-	/*
-	 * _STA is mandatory for a processor that supports hot plug
-	 */
-	if (status == AE_NOT_FOUND)
-		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-				"Processor does not support hot plug\n"));
-	else
-		ACPI_EXCEPTION((AE_INFO, status,
-				"Processor Device is not present"));
-	return 0;
+	get_online_cpus();
+	acpi_processor_unlink(device, pr);
+	put_online_cpus();
+	arch_unregister_cpu(pr->id);
+	acpi_unmap_lsapic(pr->id);
+	pr->id = -1;
 }
 
-static
-int acpi_processor_device_add(acpi_handle handle, struct acpi_device **device)
+static int acpi_processor_get_dev_info(struct acpi_device *device,
+				       struct acpihp_dev_info *info)
 {
-	acpi_handle phandle;
-	struct acpi_device *pdev;
-
-
-	if (acpi_get_parent(handle, &phandle)) {
-		return -ENODEV;
-	}
+	struct acpi_processor *pr;
 
-	if (acpi_bus_get_device(phandle, &pdev)) {
-		return -ENODEV;
-	}
+	if (!device || !acpi_driver_data(device))
+		return -EINVAL;
+	pr = acpi_driver_data(device);
 
-	if (acpi_bus_add(device, pdev, handle, ACPI_BUS_TYPE_PROCESSOR)) {
-		return -ENODEV;
-	}
+	info->type = ACPIHP_DEV_TYPE_CPU;
+	if (pr->id >= 0)
+		info->status |= ACPIHP_DEV_STATUS_STARTED;
 
 	return 0;
 }
 
-static void acpi_processor_hotplug_notify(acpi_handle handle,
-					  u32 event, void *data)
+static int acpi_processor_pre_configure(struct acpi_device *device,
+					struct acpihp_cancel_context *ctx)
 {
-	struct acpi_processor *pr;
-	struct acpi_device *device = NULL;
-	u32 ost_code = ACPI_OST_SC_NON_SPECIFIC_FAILURE; /* default */
 	int result;
+	struct acpi_processor *pr;
 
-	switch (event) {
-	case ACPI_NOTIFY_BUS_CHECK:
-	case ACPI_NOTIFY_DEVICE_CHECK:
-		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-		"Processor driver received %s event\n",
-		       (event == ACPI_NOTIFY_BUS_CHECK) ?
-		       "ACPI_NOTIFY_BUS_CHECK" : "ACPI_NOTIFY_DEVICE_CHECK"));
-
-		if (!is_processor_present(handle))
-			break;
-
-		if (!acpi_bus_get_device(handle, &device))
-			break;
-
-		result = acpi_processor_device_add(handle, &device);
-		if (result) {
-			printk(KERN_ERR PREFIX "Unable to add the device\n");
-			break;
-		}
-
-		ost_code = ACPI_OST_SC_SUCCESS;
-		break;
-
-	case ACPI_NOTIFY_EJECT_REQUEST:
-		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-				  "received ACPI_NOTIFY_EJECT_REQUEST\n"));
+	if (!device || !acpi_driver_data(device))
+		return -EINVAL;
+	pr = acpi_driver_data(device);
 
-		if (acpi_bus_get_device(handle, &device)) {
-			printk(KERN_ERR PREFIX
-				    "Device don't exist, dropping EJECT\n");
-			break;
-		}
-		pr = acpi_driver_data(device);
-		if (!pr) {
-			printk(KERN_ERR PREFIX
-				    "Driver data is NULL, dropping EJECT\n");
-			break;
-		}
+	/* Generate CPUID for hot-added CPUs */
+	if (pr->id == -1) {
+		result = acpi_map_lsapic(device->handle, &pr->id);
+		if (result)
+			goto out_unlock;
+		BUG_ON((pr->id >= nr_cpu_ids) || (pr->id < 0));
+		result = arch_register_cpu(pr->id);
+		if (result)
+			goto out_unmap;
 
-		/* REVISIT: update when eject is supported */
-		ost_code = ACPI_OST_SC_EJECT_NOT_SUPPORTED;
-		break;
+		/*
+		 * CPU got hot-plugged, but cpu_data is not initialized yet.
+		 * Set flag to let acpi_cpu_soft_notify() initialize cpu_data
+		 * by calling acpi_processor_start
+		 */
+		pr->flags.need_hotplug_init = 1;
+		dev_info(&device->dev, "CPU %d got hotplugged\n", pr->id);
+	}
 
-	default:
-		ACPI_DEBUG_PRINT((ACPI_DB_INFO,
-				  "Unsupported event [0x%x]\n", event));
+	return 0;
 
-		/* non-hotplug event; possibly handled by other handler */
-		return;
-	}
+out_unmap:
+	acpi_unmap_lsapic(pr->id);
+	pr->id = -1;
+out_unlock:
+	put_online_cpus();
 
-	/* Inform firmware that the hotplug operation has completed */
-	(void) acpi_evaluate_hotplug_ost(handle, event, ost_code, NULL);
-	return;
+	return result;
 }
 
-static acpi_status is_processor_device(acpi_handle handle)
+static int acpi_processor_configure(struct acpi_device *device,
+				    struct acpihp_cancel_context *ctx)
 {
-	struct acpi_device_info *info;
-	char *hid;
-	acpi_status status;
-
-	status = acpi_get_object_info(handle, &info);
-	if (ACPI_FAILURE(status))
-		return status;
-
-	if (info->type == ACPI_TYPE_PROCESSOR) {
-		kfree(info);
-		return AE_OK;	/* found a processor object */
-	}
+	int result;
+	struct acpi_processor *pr;
 
-	if (!(info->valid & ACPI_VALID_HID)) {
-		kfree(info);
-		return AE_ERROR;
-	}
+	if (!device || !acpi_driver_data(device))
+		return -EINVAL;
+	pr = acpi_driver_data(device);
 
-	hid = info->hardware_id.string;
-	if ((hid == NULL) || strcmp(hid, ACPI_PROCESSOR_DEVICE_HID)) {
-		kfree(info);
-		return AE_ERROR;
-	}
+	get_online_cpus();
+	result = acpi_processor_link(device, pr);
+	put_online_cpus();
 
-	kfree(info);
-	return AE_OK;	/* found a processor device object */
+	return result;
 }
 
-static acpi_status
-processor_walk_namespace_cb(acpi_handle handle,
-			    u32 lvl, void *context, void **rv)
+static void acpi_processor_post_configure(struct acpi_device *device,
+					  enum acpihp_dev_post_cmd cmd)
 {
-	acpi_status status;
-	int *action = context;
-
-	status = is_processor_device(handle);
-	if (ACPI_FAILURE(status))
-		return AE_OK;	/* not a processor; continue to walk */
-
-	switch (*action) {
-	case INSTALL_NOTIFY_HANDLER:
-		acpi_install_notify_handler(handle,
-					    ACPI_SYSTEM_NOTIFY,
-					    acpi_processor_hotplug_notify,
-					    NULL);
-		break;
-	case UNINSTALL_NOTIFY_HANDLER:
-		acpi_remove_notify_handler(handle,
-					   ACPI_SYSTEM_NOTIFY,
-					   acpi_processor_hotplug_notify);
-		break;
-	default:
-		break;
-	}
+	struct acpi_processor *pr;
+
+	BUG_ON(!device || !acpi_driver_data(device));
+	pr = acpi_driver_data(device);
 
-	/* found a processor; skip walking underneath */
-	return AE_CTRL_DEPTH;
+	if (cmd == ACPIHP_DEV_POST_CMD_COMMIT) {
+		if (!cpu_online(pr->id) && cpu_up(pr->id))
+			dev_warn(&device->dev,
+				 "fails to online CPU%d.\n", pr->id);
+	} else if (cmd == ACPIHP_DEV_POST_CMD_ROLLBACK)
+		acpi_processor_reset(device, pr);
 }
 
-static acpi_status acpi_processor_hotadd_init(struct acpi_processor *pr)
+static int acpi_processor_release(struct acpi_device *device,
+				  struct acpihp_cancel_context *ctx)
 {
-	acpi_handle handle = pr->handle;
-
-	if (!is_processor_present(handle)) {
-		return AE_ERROR;
-	}
-
-	if (acpi_map_lsapic(handle, &pr->id))
-		return AE_ERROR;
+	int result = 0;
+	struct acpi_processor *pr;
 
-	if (arch_register_cpu(pr->id)) {
-		acpi_unmap_lsapic(pr->id);
-		return AE_ERROR;
-	}
+	if (!device || !acpi_driver_data(device))
+		return -EINVAL;
+	pr = acpi_driver_data(device);
 
-	/* CPU got hot-plugged, but cpu_data is not initialized yet
-	 * Set flag to delay cpu_idle/throttling initialization
-	 * in:
-	 * acpi_processor_add()
-	 *   acpi_processor_get_info()
-	 * and do it when the CPU gets online the first time
-	 * TBD: Cleanup above functions and try to do this more elegant.
-	 */
-	printk(KERN_INFO "CPU %d got hotplugged\n", pr->id);
-	pr->flags.need_hotplug_init = 1;
+	if (cpu_online(pr->id))
+		result = cpu_down(pr->id);
 
-	return AE_OK;
+	return result;
 }
 
-static int acpi_processor_handle_eject(struct acpi_processor *pr)
+static void acpi_processor_post_release(struct acpi_device *device,
+				        enum acpihp_dev_post_cmd cmd)
 {
-	if (cpu_online(pr->id))
-		cpu_down(pr->id);
+	struct acpi_processor *pr;
 
-	arch_unregister_cpu(pr->id);
-	acpi_unmap_lsapic(pr->id);
-	return (0);
-}
-#else
-static acpi_status acpi_processor_hotadd_init(struct acpi_processor *pr)
-{
-	return AE_ERROR;
-}
-static int acpi_processor_handle_eject(struct acpi_processor *pr)
-{
-	return (-EINVAL);
-}
-#endif
+	BUG_ON(!device || !acpi_driver_data(device));
+	pr = acpi_driver_data(device);
 
-static
-void acpi_processor_install_hotplug_notify(void)
-{
-#ifdef CONFIG_ACPI_HOTPLUG_CPU
-	int action = INSTALL_NOTIFY_HANDLER;
-	acpi_walk_namespace(ACPI_TYPE_ANY,
-			    ACPI_ROOT_OBJECT,
-			    ACPI_UINT32_MAX,
-			    processor_walk_namespace_cb, NULL, &action, NULL);
-#endif
-	register_hotcpu_notifier(&acpi_cpu_notifier);
+	if (cmd == ACPIHP_DEV_POST_CMD_ROLLBACK)
+		if (!cpu_online(pr->id))
+			cpu_up(pr->id);
 }
 
-static
-void acpi_processor_uninstall_hotplug_notify(void)
+static void acpi_processor_unconfigure(struct acpi_device *device)
 {
-#ifdef CONFIG_ACPI_HOTPLUG_CPU
-	int action = UNINSTALL_NOTIFY_HANDLER;
-	acpi_walk_namespace(ACPI_TYPE_ANY,
-			    ACPI_ROOT_OBJECT,
-			    ACPI_UINT32_MAX,
-			    processor_walk_namespace_cb, NULL, &action, NULL);
-#endif
-	unregister_hotcpu_notifier(&acpi_cpu_notifier);
+	struct acpi_processor *pr;
+
+	BUG_ON(!device || !acpi_driver_data(device));
+	pr = acpi_driver_data(device);
+	acpi_processor_stop(device, pr);
+	acpi_processor_reset(device, pr);
 }
 
+static struct acpihp_dev_ops acpi_processor_hp_ops = {
+	.get_info = &acpi_processor_get_dev_info,
+	.pre_configure = &acpi_processor_pre_configure,
+	.configure = &acpi_processor_configure,
+	.post_configure = &acpi_processor_post_configure,
+	.release = &acpi_processor_release,
+	.post_release = &acpi_processor_post_release,
+	.unconfigure = &acpi_processor_unconfigure,
+};
+#endif	/* CONFIG_ACPI_HOTPLUG_CPU */
+
 /*
  * We keep the driver loaded even when ACPI is not running.
  * This is needed for the powernow-k8 driver, that works even without
  * ACPI, but needs symbols from this driver
  */
-
 static int __init acpi_processor_init(void)
 {
 	int result = 0;
@@ -940,7 +827,7 @@ static int __init acpi_processor_init(void)
 	if (result < 0)
 		return result;
 
-	acpi_processor_install_hotplug_notify();
+	register_hotcpu_notifier(&acpi_cpu_notifier);
 
 	acpi_thermal_cpufreq_init();
 
@@ -960,7 +847,7 @@ static void __exit acpi_processor_exit(void)
 
 	acpi_thermal_cpufreq_exit();
 
-	acpi_processor_uninstall_hotplug_notify();
+	unregister_hotcpu_notifier(&acpi_cpu_notifier);
 
 	acpi_bus_unregister_driver(&acpi_processor_driver);
 
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 24/28] CPU: introduce busy flag to temporarily disable CPU online sysfs interface
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (22 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 23/28] ACPIHP/processor: enhance processor driver to support new hotplug framework Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 25/28] ACPIHP/processor: reject online/offline requests when doing processor hotplug Jiang Liu
                   ` (4 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

When doing physical processor hotplug, all affected CPUs need to be
handled in atomic and shouldn't be disturbed by online/offline requests
from CPU device's online sysfs interface. So introduce a busy flag
into struct cpu to temporariliy reject requests from online sysfs
interface.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
---
 arch/ia64/include/asm/cpu.h     |    2 +-
 arch/ia64/kernel/topology.c     |   15 +++++++++------
 arch/x86/include/asm/cpu.h      |    2 +-
 arch/x86/kernel/topology.c      |   10 ++++++----
 drivers/acpi/processor_driver.c |    2 +-
 drivers/base/cpu.c              |   22 ++++++++++++++++++++++
 drivers/xen/cpu_hotplug.c       |    2 +-
 include/linux/cpu.h             |    2 ++
 8 files changed, 43 insertions(+), 14 deletions(-)

diff --git a/arch/ia64/include/asm/cpu.h b/arch/ia64/include/asm/cpu.h
index fcca30b..192fa2f 100644
--- a/arch/ia64/include/asm/cpu.h
+++ b/arch/ia64/include/asm/cpu.h
@@ -15,7 +15,7 @@ DECLARE_PER_CPU(struct ia64_cpu, cpu_devices);
 DECLARE_PER_CPU(int, cpu_state);
 
 #ifdef CONFIG_HOTPLUG_CPU
-extern int arch_register_cpu(int num);
+extern int arch_register_cpu(int num, int busy);
 extern void arch_unregister_cpu(int);
 #endif
 
diff --git a/arch/ia64/kernel/topology.c b/arch/ia64/kernel/topology.c
index c64460b..54a6dfc 100644
--- a/arch/ia64/kernel/topology.c
+++ b/arch/ia64/kernel/topology.c
@@ -40,15 +40,17 @@ EXPORT_SYMBOL_GPL(arch_fix_phys_package_id);
 
 
 #ifdef CONFIG_HOTPLUG_CPU
-int __ref arch_register_cpu(int num)
+int __ref arch_register_cpu(int num, int busy)
 {
 #ifdef CONFIG_ACPI
 	/*
 	 * If CPEI can be re-targeted or if this is not
 	 * CPEI target, then it is hotpluggable
 	 */
-	if (can_cpei_retarget() || !is_cpu_cpei_target(num))
+	if (can_cpei_retarget() || !is_cpu_cpei_target(num)) {
 		sysfs_cpus[num].cpu.hotpluggable = 1;
+		sysfs_cpus[num].cpu.busy = busy;
+	}
 	map_cpu_to_node(num, node_cpuid[num].nid);
 #endif
 	return register_cpu(&sysfs_cpus[num].cpu, num);
@@ -64,7 +66,7 @@ void __ref arch_unregister_cpu(int num)
 }
 EXPORT_SYMBOL(arch_unregister_cpu);
 #else
-static int __init arch_register_cpu(int num)
+static int __init arch_register_cpu(int num, int busy)
 {
 	return register_cpu(&sysfs_cpus[num].cpu, num);
 }
@@ -90,10 +92,11 @@ static int __init topology_init(void)
 		panic("kzalloc in topology_init failed - NR_CPUS too big?");
 
 	for_each_present_cpu(i) {
-		if((err = arch_register_cpu(i)))
-			goto out;
+		err = arch_register_cpu(i, 0);
+		if (err)
+			break;
 	}
-out:
+
 	return err;
 }
 
diff --git a/arch/x86/include/asm/cpu.h b/arch/x86/include/asm/cpu.h
index 4564c8e..724c777 100644
--- a/arch/x86/include/asm/cpu.h
+++ b/arch/x86/include/asm/cpu.h
@@ -26,7 +26,7 @@ struct x86_cpu {
 };
 
 #ifdef CONFIG_HOTPLUG_CPU
-extern int arch_register_cpu(int num);
+extern int arch_register_cpu(int num, int busy);
 extern void arch_unregister_cpu(int);
 #endif
 
diff --git a/arch/x86/kernel/topology.c b/arch/x86/kernel/topology.c
index 76ee977..c66ef53 100644
--- a/arch/x86/kernel/topology.c
+++ b/arch/x86/kernel/topology.c
@@ -35,7 +35,7 @@
 static DEFINE_PER_CPU(struct x86_cpu, cpu_devices);
 
 #ifdef CONFIG_HOTPLUG_CPU
-int __ref arch_register_cpu(int num)
+int __ref arch_register_cpu(int num, int busy)
 {
 	/*
 	 * CPU0 cannot be offlined due to several
@@ -46,8 +46,10 @@ int __ref arch_register_cpu(int num)
 	 * Also certain PCI quirks require not to enable hotplug control
 	 * for all CPU's.
 	 */
-	if (num)
+	if (num) {
 		per_cpu(cpu_devices, num).cpu.hotpluggable = 1;
+		per_cpu(cpu_devices, num).cpu.busy = busy;
+	}
 
 	return register_cpu(&per_cpu(cpu_devices, num).cpu, num);
 }
@@ -60,7 +62,7 @@ void arch_unregister_cpu(int num)
 EXPORT_SYMBOL(arch_unregister_cpu);
 #else /* CONFIG_HOTPLUG_CPU */
 
-static int __init arch_register_cpu(int num)
+static int __init arch_register_cpu(int num, int busy)
 {
 	return register_cpu(&per_cpu(cpu_devices, num).cpu, num);
 }
@@ -76,7 +78,7 @@ static int __init topology_init(void)
 #endif
 
 	for_each_present_cpu(i)
-		arch_register_cpu(i);
+		arch_register_cpu(i, 0);
 
 	return 0;
 }
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index 25079ca..447bd6d 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -702,7 +702,7 @@ static int acpi_processor_pre_configure(struct acpi_device *device,
 		if (result)
 			goto out_unlock;
 		BUG_ON((pr->id >= nr_cpu_ids) || (pr->id < 0));
-		result = arch_register_cpu(pr->id);
+		result = arch_register_cpu(pr->id, 0);
 		if (result)
 			goto out_unmap;
 
diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c
index 6345294..dc6246c 100644
--- a/drivers/base/cpu.c
+++ b/drivers/base/cpu.c
@@ -42,6 +42,11 @@ static ssize_t __ref store_online(struct device *dev,
 	ssize_t ret;
 
 	cpu_hotplug_driver_lock();
+	if (cpu->busy) {
+		ret = -EBUSY;
+		goto out;
+	}
+
 	switch (buf[0]) {
 	case '0':
 		ret = cpu_down(cpu->dev.id);
@@ -56,6 +61,8 @@ static ssize_t __ref store_online(struct device *dev,
 	default:
 		ret = -EINVAL;
 	}
+
+out:
 	cpu_hotplug_driver_unlock();
 
 	if (ret >= 0)
@@ -308,6 +315,21 @@ bool cpu_is_hotpluggable(unsigned cpu)
 }
 EXPORT_SYMBOL_GPL(cpu_is_hotpluggable);
 
+int cpu_set_busy(unsigned int id, int busy)
+{
+	int old;
+	struct device *device = get_cpu_device(id);
+	struct cpu *cpu = container_of(device, struct cpu, dev);
+
+	cpu_hotplug_driver_lock();
+	old = cpu->busy;
+	cpu->busy = busy;
+	cpu_hotplug_driver_unlock();
+
+	return old;
+}
+EXPORT_SYMBOL_GPL(cpu_set_busy);
+
 #ifdef CONFIG_GENERIC_CPU_DEVICES
 static DEFINE_PER_CPU(struct cpu, cpu_devices);
 #endif
diff --git a/drivers/xen/cpu_hotplug.c b/drivers/xen/cpu_hotplug.c
index 4dcfced..95bde8a 100644
--- a/drivers/xen/cpu_hotplug.c
+++ b/drivers/xen/cpu_hotplug.c
@@ -9,7 +9,7 @@
 static void enable_hotplug_cpu(int cpu)
 {
 	if (!cpu_present(cpu))
-		arch_register_cpu(cpu);
+		arch_register_cpu(cpu, 0);
 
 	set_cpu_present(cpu, true);
 }
diff --git a/include/linux/cpu.h b/include/linux/cpu.h
index ce7a074..557501b 100644
--- a/include/linux/cpu.h
+++ b/include/linux/cpu.h
@@ -23,12 +23,14 @@ struct device;
 struct cpu {
 	int node_id;		/* The node which contains the CPU */
 	int hotpluggable;	/* creates sysfs control file if hotpluggable */
+	int busy;
 	struct device dev;
 };
 
 extern int register_cpu(struct cpu *cpu, int num);
 extern struct device *get_cpu_device(unsigned cpu);
 extern bool cpu_is_hotpluggable(unsigned cpu);
+extern int cpu_set_busy(unsigned int cpu, int busy);
 
 extern int cpu_add_dev_attr(struct device_attribute *attr);
 extern void cpu_remove_dev_attr(struct device_attribute *attr);
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 25/28] ACPIHP/processor: reject online/offline requests when doing processor hotplug
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (23 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 24/28] CPU: introduce busy flag to temporarily disable CPU online sysfs interface Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 26/28] ACPI/processor: save parsed APIC ID in processor driver data structure Jiang Liu
                   ` (3 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

When doing physical processor hotplug, all affected CPUs should be
handled in atomic, shouldn't be disturbed by online/offline requests
from CPU device's online sysfs interface. For example, it's fatal
if a CPU is onlined through CPU device's online sysfs interface
between the hotplug driver offlines all affected CPUs and powers the
physical processor off.

So temporarily reject online/offline requests from CPU device's online
sysfs interface by setting the busy flag when doing physical processor
hotplug.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
---
 drivers/acpi/processor_driver.c |   23 +++++++++++++++++++++--
 1 file changed, 21 insertions(+), 2 deletions(-)

diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index 447bd6d..51e3993 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -702,7 +702,7 @@ static int acpi_processor_pre_configure(struct acpi_device *device,
 		if (result)
 			goto out_unlock;
 		BUG_ON((pr->id >= nr_cpu_ids) || (pr->id < 0));
-		result = arch_register_cpu(pr->id, 0);
+		result = arch_register_cpu(pr->id, 1);
 		if (result)
 			goto out_unmap;
 
@@ -755,10 +755,26 @@ static void acpi_processor_post_configure(struct acpi_device *device,
 		if (!cpu_online(pr->id) && cpu_up(pr->id))
 			dev_warn(&device->dev,
 				 "fails to online CPU%d.\n", pr->id);
+		cpu_set_busy(pr->id, 0);
 	} else if (cmd == ACPIHP_DEV_POST_CMD_ROLLBACK)
 		acpi_processor_reset(device, pr);
 }
 
+static int acpi_processor_pre_release(struct acpi_device *device,
+				      struct acpihp_cancel_context *ctx)
+{
+	int result;
+	struct acpi_processor *pr;
+
+	if (!device || !acpi_driver_data(device))
+		return -EINVAL;
+	pr = acpi_driver_data(device);
+
+	result = cpu_set_busy(pr->id, 1);
+
+	return result ? -EBUSY : 0;
+}
+
 static int acpi_processor_release(struct acpi_device *device,
 				  struct acpihp_cancel_context *ctx)
 {
@@ -783,9 +799,11 @@ static void acpi_processor_post_release(struct acpi_device *device,
 	BUG_ON(!device || !acpi_driver_data(device));
 	pr = acpi_driver_data(device);
 
-	if (cmd == ACPIHP_DEV_POST_CMD_ROLLBACK)
+	if (cmd == ACPIHP_DEV_POST_CMD_ROLLBACK) {
 		if (!cpu_online(pr->id))
 			cpu_up(pr->id);
+		cpu_set_busy(pr->id, 0);
+	}
 }
 
 static void acpi_processor_unconfigure(struct acpi_device *device)
@@ -803,6 +821,7 @@ static struct acpihp_dev_ops acpi_processor_hp_ops = {
 	.pre_configure = &acpi_processor_pre_configure,
 	.configure = &acpi_processor_configure,
 	.post_configure = &acpi_processor_post_configure,
+	.pre_release = &acpi_processor_pre_release,
 	.release = &acpi_processor_release,
 	.post_release = &acpi_processor_post_release,
 	.unconfigure = &acpi_processor_unconfigure,
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 26/28] ACPI/processor: save parsed APIC ID in processor driver data structure
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (24 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 25/28] ACPIHP/processor: reject online/offline requests when doing processor hotplug Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 27/28] x86: simplify _acpi_map_lsapic() Jiang Liu
                   ` (2 subsequent siblings)
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

Enhance ACPI processor driver to save parsed APIC ID in processor driver
data structure, and avoid reparsing again in _acpi_map_lsapic().
This change also makes fake CPU hotplug possible because we have no
dependency on _MAT method now.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
---
 arch/ia64/kernel/acpi.c         |   38 ++++----------------------------------
 arch/x86/kernel/acpi/boot.c     |   38 +++-----------------------------------
 drivers/acpi/internal.h         |    2 ++
 drivers/acpi/processor_core.c   |   26 +++++++++++++++++++++-----
 drivers/acpi/processor_driver.c |   15 +++++++++------
 include/acpi/processor.h        |    1 +
 include/linux/acpi.h            |    2 +-
 7 files changed, 41 insertions(+), 81 deletions(-)

diff --git a/arch/ia64/kernel/acpi.c b/arch/ia64/kernel/acpi.c
index 44057885..f0da941 100644
--- a/arch/ia64/kernel/acpi.c
+++ b/arch/ia64/kernel/acpi.c
@@ -880,40 +880,10 @@ __init void prefill_possible_map(void)
 		set_cpu_possible(i, true);
 }
 
-static int __cpuinit _acpi_map_lsapic(acpi_handle handle, int *pcpu)
+static int __cpuinit _acpi_map_lsapic(acpi_handle handle, int physid, int *pcpu)
 {
-	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
-	union acpi_object *obj;
-	struct acpi_madt_local_sapic *lsapic;
 	cpumask_t tmp_map;
-	int cpu, physid;
-
-	if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer)))
-		return -EINVAL;
-
-	if (!buffer.length || !buffer.pointer)
-		return -EINVAL;
-
-	obj = buffer.pointer;
-	if (obj->type != ACPI_TYPE_BUFFER)
-	{
-		kfree(buffer.pointer);
-		return -EINVAL;
-	}
-
-	lsapic = (struct acpi_madt_local_sapic *)obj->buffer.pointer;
-
-	if ((lsapic->header.type != ACPI_MADT_TYPE_LOCAL_SAPIC) ||
-	    (!(lsapic->lapic_flags & ACPI_MADT_ENABLED))) {
-		kfree(buffer.pointer);
-		return -EINVAL;
-	}
-
-	physid = ((lsapic->id << 8) | (lsapic->eid));
-
-	kfree(buffer.pointer);
-	buffer.length = ACPI_ALLOCATE_BUFFER;
-	buffer.pointer = NULL;
+	int cpu;
 
 	cpumask_complement(&tmp_map, cpu_present_mask);
 	cpu = cpumask_first(&tmp_map);
@@ -932,9 +902,9 @@ static int __cpuinit _acpi_map_lsapic(acpi_handle handle, int *pcpu)
 }
 
 /* wrapper to silence section mismatch warning */
-int __ref acpi_map_lsapic(acpi_handle handle, int *pcpu)
+int __ref acpi_map_lsapic(acpi_handle handle, int physid, int *pcpu)
 {
-	return _acpi_map_lsapic(handle, pcpu);
+	return _acpi_map_lsapic(handle, physid, pcpu);
 }
 EXPORT_SYMBOL(acpi_map_lsapic);
 
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index e651f7a..5e76952 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -608,44 +608,12 @@ static void __cpuinit acpi_map_cpu2node(acpi_handle handle, int cpu, int physid)
 #endif
 }
 
-static int __cpuinit _acpi_map_lsapic(acpi_handle handle, int *pcpu)
+static int __cpuinit _acpi_map_lsapic(acpi_handle handle, int physid, int *pcpu)
 {
-	struct acpi_buffer buffer = { ACPI_ALLOCATE_BUFFER, NULL };
-	union acpi_object *obj;
-	struct acpi_madt_local_apic *lapic;
 	cpumask_var_t tmp_map, new_map;
-	u8 physid;
 	int cpu;
 	int retval = -ENOMEM;
 
-	if (ACPI_FAILURE(acpi_evaluate_object(handle, "_MAT", NULL, &buffer)))
-		return -EINVAL;
-
-	if (!buffer.length || !buffer.pointer)
-		return -EINVAL;
-
-	obj = buffer.pointer;
-	if (obj->type != ACPI_TYPE_BUFFER ||
-	    obj->buffer.length < sizeof(*lapic)) {
-		kfree(buffer.pointer);
-		return -EINVAL;
-	}
-
-	lapic = (struct acpi_madt_local_apic *)obj->buffer.pointer;
-
-	if (lapic->header.type != ACPI_MADT_TYPE_LOCAL_APIC ||
-	    !(lapic->lapic_flags & ACPI_MADT_ENABLED)) {
-		kfree(buffer.pointer);
-		return -EINVAL;
-	}
-
-	physid = lapic->id;
-
-	kfree(buffer.pointer);
-	buffer.length = ACPI_ALLOCATE_BUFFER;
-	buffer.pointer = NULL;
-	lapic = NULL;
-
 	if (!alloc_cpumask_var(&tmp_map, GFP_KERNEL))
 		goto out;
 
@@ -683,9 +651,9 @@ out:
 }
 
 /* wrapper to silence section mismatch warning */
-int __ref acpi_map_lsapic(acpi_handle handle, int *pcpu)
+int __ref acpi_map_lsapic(acpi_handle handle, int physid, int *pcpu)
 {
-	return _acpi_map_lsapic(handle, pcpu);
+	return _acpi_map_lsapic(handle, physid, pcpu);
 }
 EXPORT_SYMBOL(acpi_map_lsapic);
 
diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h
index 6f0632d..036dba11 100644
--- a/drivers/acpi/internal.h
+++ b/drivers/acpi/internal.h
@@ -47,6 +47,8 @@ int acpi_bus_init_power(struct acpi_device *device);
 
 int acpi_wakeup_device_init(void);
 void acpi_early_processor_set_pdc(void);
+int acpi_get_apicid(acpi_handle handle, int type, u32 acpi_id);
+int acpi_map_cpuid(int apic_id, u32 acpi_id);
 
 /* --------------------------------------------------------------------------
                                   Embedded Controller
diff --git a/drivers/acpi/processor_core.c b/drivers/acpi/processor_core.c
index eff7222..21217e5 100644
--- a/drivers/acpi/processor_core.c
+++ b/drivers/acpi/processor_core.c
@@ -163,16 +163,23 @@ exit:
 	return apic_id;
 }
 
-int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id)
+int acpi_get_apicid(acpi_handle handle, int type, u32 acpi_id)
 {
-#ifdef CONFIG_SMP
-	int i;
-#endif
-	int apic_id = -1;
+	int apic_id;
 
 	apic_id = map_mat_entry(handle, type, acpi_id);
 	if (apic_id == -1)
 		apic_id = map_madt_entry(type, acpi_id);
+
+	return apic_id;
+}
+
+int acpi_map_cpuid(int apic_id, u32 acpi_id)
+{
+#ifdef CONFIG_SMP
+	int i;
+#endif
+
 	if (apic_id == -1) {
 		/*
 		 * On UP processor, there is no _MAT or MADT table.
@@ -212,6 +219,15 @@ int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id)
 #endif
 	return -1;
 }
+
+int acpi_get_cpuid(acpi_handle handle, int type, u32 acpi_id)
+{
+	int apic_id;
+
+	apic_id = acpi_get_apicid(handle, type, acpi_id);
+
+	return acpi_map_cpuid(apic_id, acpi_id);
+}
 EXPORT_SYMBOL_GPL(acpi_get_cpuid);
 
 static bool __init processor_physically_present(acpi_handle handle)
diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index 51e3993..2346b81 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -57,6 +57,7 @@
 #include <acpi/acpi_drivers.h>
 #include <acpi/processor.h>
 #include <acpi/acpi_hotplug.h>
+#include "internal.h"
 
 #define PREFIX "ACPI: "
 
@@ -303,7 +304,8 @@ static int acpi_processor_get_info(struct acpi_device *device)
 		device_declaration = 1;
 		pr->acpi_id = value;
 	}
-	cpu_index = acpi_get_cpuid(pr->handle, device_declaration, pr->acpi_id);
+	pr->apic_id = acpi_get_apicid(pr->handle, device_declaration, pr->acpi_id);
+	cpu_index = acpi_map_cpuid(pr->apic_id, pr->acpi_id);
 
 	/* Handle UP system running SMP kernel, with no LAPIC in MADT */
 	if (!cpu0_initialized && (cpu_index == -1) &&
@@ -689,7 +691,7 @@ static int acpi_processor_get_dev_info(struct acpi_device *device,
 static int acpi_processor_pre_configure(struct acpi_device *device,
 					struct acpihp_cancel_context *ctx)
 {
-	int result;
+	int result = -EINVAL;
 	struct acpi_processor *pr;
 
 	if (!device || !acpi_driver_data(device))
@@ -698,9 +700,11 @@ static int acpi_processor_pre_configure(struct acpi_device *device,
 
 	/* Generate CPUID for hot-added CPUs */
 	if (pr->id == -1) {
-		result = acpi_map_lsapic(device->handle, &pr->id);
+		if (pr->apic_id == -1)
+			goto out;
+		result = acpi_map_lsapic(device->handle, pr->apic_id, &pr->id);
 		if (result)
-			goto out_unlock;
+			goto out;
 		BUG_ON((pr->id >= nr_cpu_ids) || (pr->id < 0));
 		result = arch_register_cpu(pr->id, 1);
 		if (result)
@@ -720,8 +724,7 @@ static int acpi_processor_pre_configure(struct acpi_device *device,
 out_unmap:
 	acpi_unmap_lsapic(pr->id);
 	pr->id = -1;
-out_unlock:
-	put_online_cpus();
+out:
 
 	return result;
 }
diff --git a/include/acpi/processor.h b/include/acpi/processor.h
index ae7d94e..da65f1f 100644
--- a/include/acpi/processor.h
+++ b/include/acpi/processor.h
@@ -200,6 +200,7 @@ struct acpi_processor_flags {
 struct acpi_processor {
 	acpi_handle handle;
 	u32 acpi_id;
+	int apic_id;
 	u32 id;
 	u32 pblk;
 	int performance_platform_limit;
diff --git a/include/linux/acpi.h b/include/linux/acpi.h
index 90be989..774ce2b 100644
--- a/include/linux/acpi.h
+++ b/include/linux/acpi.h
@@ -101,7 +101,7 @@ void acpi_numa_arch_fixup(void);
 
 #ifdef CONFIG_ACPI_HOTPLUG_CPU
 /* Arch dependent functions for cpu hotplug support */
-int acpi_map_lsapic(acpi_handle handle, int *pcpu);
+int acpi_map_lsapic(acpi_handle handle, int physid, int *pcpu);
 int acpi_unmap_lsapic(int cpu);
 #endif /* CONFIG_ACPI_HOTPLUG_CPU */
 
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 27/28] x86: simplify _acpi_map_lsapic()
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (25 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 26/28] ACPI/processor: save parsed APIC ID in processor driver data structure Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-06 15:27 ` [RFC PATCH v3 28/28] ACPI/processor: serialize call to acpi_map/unmap_lsapic Jiang Liu
  2012-10-07 21:47 ` [RFC PATCH v3 00/28] ACPI based system device hotplug framework Rafael J. Wysocki
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

Simplify implementation of function _acpi_map_lsapic().

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
---
 arch/x86/include/asm/mpspec.h |    2 +-
 arch/x86/kernel/acpi/boot.c   |   44 +++++++++--------------------------------
 arch/x86/kernel/apic/apic.c   |    8 +++++---
 3 files changed, 15 insertions(+), 39 deletions(-)

diff --git a/arch/x86/include/asm/mpspec.h b/arch/x86/include/asm/mpspec.h
index 3e2f42a..f1d6487 100644
--- a/arch/x86/include/asm/mpspec.h
+++ b/arch/x86/include/asm/mpspec.h
@@ -94,7 +94,7 @@ static inline void early_reserve_e820_mpc_new(void) { }
 #define default_get_smp_config x86_init_uint_noop
 #endif
 
-void __cpuinit generic_processor_info(int apicid, int version);
+int __cpuinit generic_processor_info(int apicid, int version);
 #ifdef CONFIG_ACPI
 extern void mp_register_ioapic(int id, u32 address, u32 gsi_base);
 extern void mp_override_legacy_irq(u8 bus_irq, u8 polarity, u8 trigger,
diff --git a/arch/x86/kernel/acpi/boot.c b/arch/x86/kernel/acpi/boot.c
index 5e76952..f354446 100644
--- a/arch/x86/kernel/acpi/boot.c
+++ b/arch/x86/kernel/acpi/boot.c
@@ -195,24 +195,24 @@ static int __init acpi_parse_madt(struct acpi_table_header *table)
 	return 0;
 }
 
-static void __cpuinit acpi_register_lapic(int id, u8 enabled)
+static int __cpuinit acpi_register_lapic(int id, u8 enabled)
 {
 	unsigned int ver = 0;
 
 	if (id >= (MAX_LOCAL_APIC-1)) {
 		printk(KERN_INFO PREFIX "skipped apicid that is too big\n");
-		return;
+		return -1;
 	}
 
 	if (!enabled) {
 		++disabled_cpus;
-		return;
+		return -1;
 	}
 
 	if (boot_cpu_physical_apicid != -1U)
 		ver = apic_version[boot_cpu_physical_apicid];
 
-	generic_processor_info(id, ver);
+	return generic_processor_info(id, ver);
 }
 
 static int __init
@@ -610,44 +610,19 @@ static void __cpuinit acpi_map_cpu2node(acpi_handle handle, int cpu, int physid)
 
 static int __cpuinit _acpi_map_lsapic(acpi_handle handle, int physid, int *pcpu)
 {
-	cpumask_var_t tmp_map, new_map;
 	int cpu;
-	int retval = -ENOMEM;
 
-	if (!alloc_cpumask_var(&tmp_map, GFP_KERNEL))
-		goto out;
-
-	if (!alloc_cpumask_var(&new_map, GFP_KERNEL))
-		goto free_tmp_map;
-
-	cpumask_copy(tmp_map, cpu_present_mask);
-	acpi_register_lapic(physid, ACPI_MADT_ENABLED);
-
-	/*
-	 * If acpi_register_lapic successfully generates a new logical cpu
-	 * number, then the following will get us exactly what was mapped
-	 */
-	cpumask_andnot(new_map, cpu_present_mask, tmp_map);
-	if (cpumask_empty(new_map)) {
+	cpu = acpi_register_lapic(physid, ACPI_MADT_ENABLED);
+	if (cpu == -1) {
 		printk ("Unable to map lapic to logical cpu number\n");
-		retval = -EINVAL;
-		goto free_new_map;
+		return -EINVAL;
 	}
 
 	acpi_processor_set_pdc(handle);
-
-	cpu = cpumask_first(new_map);
 	acpi_map_cpu2node(handle, cpu, physid);
-
 	*pcpu = cpu;
-	retval = 0;
-
-free_new_map:
-	free_cpumask_var(new_map);
-free_tmp_map:
-	free_cpumask_var(tmp_map);
-out:
-	return retval;
+
+	return 0;
 }
 
 /* wrapper to silence section mismatch warning */
@@ -665,7 +640,6 @@ int acpi_unmap_lsapic(int cpu)
 
 	return (0);
 }
-
 EXPORT_SYMBOL(acpi_unmap_lsapic);
 #endif				/* CONFIG_ACPI_HOTPLUG_CPU */
 
diff --git a/arch/x86/kernel/apic/apic.c b/arch/x86/kernel/apic/apic.c
index b17416e..9fdc293 100644
--- a/arch/x86/kernel/apic/apic.c
+++ b/arch/x86/kernel/apic/apic.c
@@ -2030,7 +2030,7 @@ void disconnect_bsp_APIC(int virt_wire_setup)
 	apic_write(APIC_LVT1, value);
 }
 
-void __cpuinit generic_processor_info(int apicid, int version)
+int __cpuinit generic_processor_info(int apicid, int version)
 {
 	int cpu, max = nr_cpu_ids;
 	bool boot_cpu_detected = physid_isset(boot_cpu_physical_apicid,
@@ -2050,7 +2050,7 @@ void __cpuinit generic_processor_info(int apicid, int version)
 			"  Processor %d/0x%x ignored.\n", max, thiscpu, apicid);
 
 		disabled_cpus++;
-		return;
+		return -1;
 	}
 
 	if (num_processors >= nr_cpu_ids) {
@@ -2061,7 +2061,7 @@ void __cpuinit generic_processor_info(int apicid, int version)
 			"  Processor %d/0x%x ignored.\n", max, thiscpu, apicid);
 
 		disabled_cpus++;
-		return;
+		return -1;
 	}
 
 	num_processors++;
@@ -2106,6 +2106,8 @@ void __cpuinit generic_processor_info(int apicid, int version)
 #endif
 	set_cpu_possible(cpu, true);
 	set_cpu_present(cpu, true);
+
+	return cpu;
 }
 
 int hard_smp_processor_id(void)
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* [RFC PATCH v3 28/28] ACPI/processor: serialize call to acpi_map/unmap_lsapic
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (26 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 27/28] x86: simplify _acpi_map_lsapic() Jiang Liu
@ 2012-10-06 15:27 ` Jiang Liu
  2012-10-07 21:47 ` [RFC PATCH v3 00/28] ACPI based system device hotplug framework Rafael J. Wysocki
  28 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-06 15:27 UTC (permalink / raw)
  To: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi
  Cc: Hanjun Guo, Yijing Wang, Gong Chen, Jiang Liu, Tony Luck,
	Huang Ying, Bob Moore, Len Brown, Srivatsa S . Bhat,
	Bjorn Helgaas, linux-kernel, linux-acpi, linux-pci

Add a mutex to serialize call to acpi_map/unmap_lsapic.

Signed-off-by: Jiang Liu <jiang.liu@huawei.com>
---
 drivers/acpi/processor_driver.c |    7 +++++++
 1 file changed, 7 insertions(+)

diff --git a/drivers/acpi/processor_driver.c b/drivers/acpi/processor_driver.c
index 2346b81..9fc20b4 100644
--- a/drivers/acpi/processor_driver.c
+++ b/drivers/acpi/processor_driver.c
@@ -108,6 +108,7 @@ static struct acpi_driver acpi_processor_driver = {
 	.drv.pm = &acpi_processor_pm,
 };
 
+static DEFINE_MUTEX(acpi_processor_mutex);
 static DEFINE_PER_CPU(void *, processor_device_array);
 
 DEFINE_PER_CPU(struct acpi_processor *, processors);
@@ -668,7 +669,9 @@ static void acpi_processor_reset(struct acpi_device *device, struct acpi_process
 	acpi_processor_unlink(device, pr);
 	put_online_cpus();
 	arch_unregister_cpu(pr->id);
+	mutex_lock(&acpi_processor_mutex);
 	acpi_unmap_lsapic(pr->id);
+	mutex_unlock(&acpi_processor_mutex);
 	pr->id = -1;
 }
 
@@ -702,7 +705,9 @@ static int acpi_processor_pre_configure(struct acpi_device *device,
 	if (pr->id == -1) {
 		if (pr->apic_id == -1)
 			goto out;
+		mutex_lock(&acpi_processor_mutex);
 		result = acpi_map_lsapic(device->handle, pr->apic_id, &pr->id);
+		mutex_unlock(&acpi_processor_mutex);
 		if (result)
 			goto out;
 		BUG_ON((pr->id >= nr_cpu_ids) || (pr->id < 0));
@@ -722,7 +727,9 @@ static int acpi_processor_pre_configure(struct acpi_device *device,
 	return 0;
 
 out_unmap:
+	mutex_lock(&acpi_processor_mutex);
 	acpi_unmap_lsapic(pr->id);
+	mutex_unlock(&acpi_processor_mutex);
 	pr->id = -1;
 out:
 
-- 
1.7.9.5


^ permalink raw reply related	[flat|nested] 31+ messages in thread

* Re: [RFC PATCH v3 00/28] ACPI based system device hotplug framework
  2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
                   ` (27 preceding siblings ...)
  2012-10-06 15:27 ` [RFC PATCH v3 28/28] ACPI/processor: serialize call to acpi_map/unmap_lsapic Jiang Liu
@ 2012-10-07 21:47 ` Rafael J. Wysocki
  2012-10-08 15:45   ` Jiang Liu
  28 siblings, 1 reply; 31+ messages in thread
From: Rafael J. Wysocki @ 2012-10-07 21:47 UTC (permalink / raw)
  To: Jiang Liu
  Cc: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi, Hanjun Guo, Yijing Wang, Gong Chen,
	Jiang Liu, Tony Luck, Huang Ying, Bob Moore, Len Brown,
	Srivatsa S . Bhat, Bjorn Helgaas, linux-kernel, linux-acpi,
	linux-pci

Hi,

On Saturday 06 of October 2012 23:27:08 Jiang Liu wrote:
> The patchset is based on "5f3d2f2e1a63679cf1c4a4210f2f1cc2f335bef6"
> from git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
> And you pull the changeset from git://github.com/jiangliu/linux.git acpihp
> 
> Modern high-end server may support advanced hotplug features for system
> devices, including physical processor, memory board, IO extension board
> and/or computer node. The ACPI specifications have provided standard
> interfaces between firmware and OS to support device hotplug at runtime.
> This patch series provide an ACPI based hotplug framework to support system
> device hotplug at runtime, which will replace current existing ACPI device
> driver based CPU/memory/CONTAINER hotplug mechanism.
> 
> The new ACPI system device hotplug framework has following design goals:
> 1) Provide a mechanism to detect hotplug slots by checking ACPI _EJ0 method,
> ACPI PRCT (platform RAS capabilities table) and other platform specific
> mechanisms.

Which of the patches does that?

> 2) Unify the way to enumerate ACPI based hotplug slots. All hotplug slots
> will be enumerated by the enumeration driver

What is the enumeration driver?

> , instead of by ACPI device drivers.
> 3) Unify the way to handle ACPI hotplug events. All ACPI hotplug events
> for system devices will be handled by a generic ACPI hotplug driver,
> instead of handled by ACPI device drivers.

What are "system devices"

> 4) Solve dependencies among hotplug slots. You need first to remove the
> memory device before removing a physical processor if a hotpluggable memory
> device is connected to a hotpluggable physical processor.
> 5) Provide better error handling and recover.
> 6) Provide interface to cancel ongoing hotplug operations. It may take a
> very long time to remove a memory device, so provide interface to cancel
> the operation. This is important especially for memory hot-removal.
> 7) Support new RAS features, such as socket/memory migration.
> 8) Provide better user interfaces to the hotplug functionalities.
> 9) Trigger hotplug events/operations by software. This feature is useful
>    for hardware fault management and power saving.

Well, this may or may not be OK.  I'm going to review the patches, but I can't
really say when it's going to happen (not really soon for sure).

> The new ACPI based hotplug framework is modelled after PCI hotplug
> architecture, which uses a hotplug driver to manage hotplug operations
> and different device drivers to manage individual devices.
> 
> The new framework contains three main components as follow:
> 1) ACPI system device hotplug slot enumerator, which enumerates all ACPI
>    hotplug slots according to platform specific knowledges. And it also
>    provides callbacks to control each slot, such as poweron, poweroff,
>    get_status etc. Currently there are two built-in methods to detect
>    hotplug slots.
> 1.a) Detecting hotplug slot by checking _EJ0 method.
> 1.b) Faking hotplug slot. Use "modprobe acpihp_slot fake_slot=0xnn",
>      0x1 for CONTAINER, 0x2 for memory device, 0x4 for CPU and 0x8 PCI host
>      bridge. The fake slot enumerator will treat each assigned ACPI device
>      as a hotplug slot, so it could be used for testing on hardware platforms
>      without hardware hotplug capabilities.
> 2) ACPI system device hotplug driver, which implements a platform indenpendent
>    state machine for hotplug slots and also provides sysfs based user
>    interfaces.
> 3) Enhanced ACPI device drivers for CONTAINER, CPU, Memroy and PCI host bridge.
>    All logic to handle hotplug events has been moved from these ACPI device
>    driver into the hotplug driver. Now these ACPI device dirvers only take
>    resposibilities configure/unconfigure corresponding system devices when
>    hotplug happens.
> 
> Now 1), 2) and part of 2) are ready, we still need to enhance mem_hotplug
> and pci_root drivers to support new hotplug framework.
> 
> On my laptop with fake slot enumerator, we got:
> root@cat:/sys/devices/LNXSYSTM:00/acpihp# ls
> CPU00  CPU01  CPU02  CPU03  CPU04  CPU05  CPU06  CPU07  IOX00
> root@cat:/sys/devices/LNXSYSTM:00/acpihp# cd CPU07/
> root@cat:/sys/devices/LNXSYSTM:00/acpihp/CPU07# ls
> capabilities  connect     device  object  state   subsystem  uevent
> configure     dependency  enable  power   status  type
> 
> /* Hot-remove CPU07 */
> root@cat:/sys/devices/LNXSYSTM:00/acpihp/CPU07# echo 0 > enable
> /* Hot-add CPU07 */
> root@cat:/sys/devices/LNXSYSTM:00/acpihp/CPU07# echo 1 > configure
> 
> V2->V3 changes:
> 1) Fix minor bugs and refine code
> 2) Implement fake slot enumerator for testing
> 3) Enhance ACPI container driver to support new hotplug framework
> 4) Enhance ACPI processor driver to support new hotplug framework
> 5) Resolve dependencies among slots by evaluating _EDL method.
> TODO:
> 1) Enhance ACPI mem_hotplug driver to support new hotplug framework
> 2) Enhance ACPI pci_root driver to support new PCI host bridge hotplug
>    based on Yinghai's work
> 
> V1->V2 changes:
> 1) implement the ACPI hotplug driver
> 2) enhance ACPI container driver to support new hotplug framework
> TODO:
> 1) handle ACPI hotplug events in ACPI hotplug driver
> 2) handle dependencies among slots by evaluating ACPI _EDL
> 3) enhance ACPI processor, memory and PCI root drivers to support new
>    hotplug framework
> 4) develop an ACPI driver for IOAPIC
> 5) thorough tests:)
> 
> Jiang Liu (28):
>   ACPIHP: introduce a framework for ACPI based system device hotplug
>   ACPIHP: ACPI system device hotplug slot enumerator driver
>   ACPIHP: detect ACPI hotplug slots by checking ACPI _EJ0 method
>   ACPIHP: implement a fake ACPI system device hotplug slot enumerator
>   ACPI: introduce interfaces to manage ACPI device reference count
>   ACPIHP: introduce interfaces to scan and walk ACPI devices connecting
>     to a slot
>   ACPIHP: use klist to manage ACPI devices connecting to a slot
>   ACPIHP: add callbacks into acpi_device_ops to support new hotplug
>     framework
>   ACPIHP: provide interfaces to associate driver specific data with
>     slots
>   ACPIHP: implement utility functions to support system device hotplug
>   ACPIHP: implement skeleton of ACPI system device hotplug driver
>   ACPIHP: analyse dependencies among ACPI hotplug slots
>   ACPIHP: provide interface to cancel inprogress hotplug operations
>   ACPIHP: configure/unconfigure system devices connecting to a hotplug
>     slot
>   ACPIHP: implement the state machine for ACPI hotplug slots
>   ACPIHP: implement sysfs interfaces for ACPI system device hotplug
>   ACPIHP: block ACPI device driver unloading when executing hotplug
>     operations
>   ACPIHP/container: change ACPI container driver to support new hotplug
>     framework
>   ACPIHP/container: move container.c into drivers/acpi/hotplug
>   ACPI/processor: remove dead code from processor_driver.c
>   ACPIHP/processor: reorganize ACPI processor driver for new hotplug
>     framework
>   ACPIHP/processor: protect accesses to device->driver_data
>   ACPIHP/processor: enhance processor driver to support new hotplug
>     framework
>   CPU: introduce busy flag to temporarily disable CPU online sysfs
>     interface
>   ACPIHP/processor: reject online/offline requests when doing processor
>     hotplug
>   ACPI/processor: save parsed APIC ID in processor driver data
>     structure
>   x86: simplify _acpi_map_lsapic()
>   ACPI/processor: serialize call to acpi_map/unmap_lsapic
> 
>  arch/ia64/include/asm/cpu.h            |    2 +-
>  arch/ia64/kernel/acpi.c                |   38 +-
>  arch/ia64/kernel/topology.c            |   15 +-
>  arch/x86/include/asm/cpu.h             |    2 +-
>  arch/x86/include/asm/mpspec.h          |    2 +-
>  arch/x86/kernel/acpi/boot.c            |   82 +---
>  arch/x86/kernel/apic/apic.c            |    8 +-
>  arch/x86/kernel/topology.c             |   10 +-
>  drivers/acpi/Kconfig                   |   69 ++-
>  drivers/acpi/Makefile                  |    3 +-
>  drivers/acpi/bus.c                     |   22 +-
>  drivers/acpi/container.c               |  296 -----------
>  drivers/acpi/hotplug/Makefile          |   21 +
>  drivers/acpi/hotplug/acpihp.h          |   37 ++
>  drivers/acpi/hotplug/acpihp_drv.h      |  107 ++++
>  drivers/acpi/hotplug/cancel.c          |  174 +++++++
>  drivers/acpi/hotplug/configure.c       |  354 ++++++++++++++
>  drivers/acpi/hotplug/container.c       |  135 +++++
>  drivers/acpi/hotplug/core.c            |  842 ++++++++++++++++++++++++++++++++
>  drivers/acpi/hotplug/dependency.c      |  249 ++++++++++
>  drivers/acpi/hotplug/device.c          |  208 ++++++++
>  drivers/acpi/hotplug/drv_main.c        |  351 +++++++++++++
>  drivers/acpi/hotplug/slot.c            |  421 ++++++++++++++++
>  drivers/acpi/hotplug/slot_ej0.c        |  153 ++++++
>  drivers/acpi/hotplug/slot_fake.c       |  177 +++++++
>  drivers/acpi/hotplug/state_machine.c   |  631 ++++++++++++++++++++++++
>  drivers/acpi/hotplug/sysfs.c           |  245 ++++++++++
>  drivers/acpi/internal.h                |    7 +
>  drivers/acpi/processor_core.c          |   26 +-
>  drivers/acpi/processor_driver.c        |  557 ++++++++++-----------
>  drivers/acpi/scan.c                    |   17 +-
>  drivers/base/cpu.c                     |   22 +
>  drivers/gpu/drm/i915/intel_opregion.c  |    2 +
>  drivers/gpu/drm/nouveau/nouveau_acpi.c |    1 +
>  drivers/pci/hotplug/acpiphp_glue.c     |    9 +-
>  drivers/pci/hotplug/acpiphp_ibm.c      |    5 +-
>  drivers/pci/hotplug/sgi_hotplug.c      |    6 +-
>  drivers/platform/x86/thinkpad_acpi.c   |    2 +
>  drivers/pnp/pnpacpi/core.c             |   23 +-
>  drivers/xen/cpu_hotplug.c              |    2 +-
>  include/acpi/acpi_bus.h                |   21 +-
>  include/acpi/acpi_hotplug.h            |  323 ++++++++++++
>  include/acpi/container.h               |   12 -
>  include/acpi/processor.h               |    3 +
>  include/linux/acpi.h                   |    2 +-
>  include/linux/cpu.h                    |    2 +
>  46 files changed, 4923 insertions(+), 773 deletions(-)
>  delete mode 100644 drivers/acpi/container.c
>  create mode 100644 drivers/acpi/hotplug/Makefile
>  create mode 100644 drivers/acpi/hotplug/acpihp.h
>  create mode 100644 drivers/acpi/hotplug/acpihp_drv.h
>  create mode 100644 drivers/acpi/hotplug/cancel.c
>  create mode 100644 drivers/acpi/hotplug/configure.c
>  create mode 100644 drivers/acpi/hotplug/container.c
>  create mode 100644 drivers/acpi/hotplug/core.c
>  create mode 100644 drivers/acpi/hotplug/dependency.c
>  create mode 100644 drivers/acpi/hotplug/device.c
>  create mode 100644 drivers/acpi/hotplug/drv_main.c
>  create mode 100644 drivers/acpi/hotplug/slot.c
>  create mode 100644 drivers/acpi/hotplug/slot_ej0.c
>  create mode 100644 drivers/acpi/hotplug/slot_fake.c
>  create mode 100644 drivers/acpi/hotplug/state_machine.c
>  create mode 100644 drivers/acpi/hotplug/sysfs.c
>  create mode 100644 include/acpi/acpi_hotplug.h
>  delete mode 100644 include/acpi/container.h

And I seriously think this is too much material for a single patchset.

Please consider splitting it into smaller and well-documented series of
patches, so that the reviewers can understand things more easily.

Thanks,
Rafael


-- 
I speak only for myself.
Rafael J. Wysocki, Intel Open Source Technology Center.

^ permalink raw reply	[flat|nested] 31+ messages in thread

* Re: [RFC PATCH v3 00/28] ACPI based system device hotplug framework
  2012-10-07 21:47 ` [RFC PATCH v3 00/28] ACPI based system device hotplug framework Rafael J. Wysocki
@ 2012-10-08 15:45   ` Jiang Liu
  0 siblings, 0 replies; 31+ messages in thread
From: Jiang Liu @ 2012-10-08 15:45 UTC (permalink / raw)
  To: Rafael J. Wysocki
  Cc: Yinghai Lu, Yasuaki Ishimatsu, Kenji Kaneshige, Wen Congyang,
	Tang Chen, Taku Izumi, Hanjun Guo, Yijing Wang, Gong Chen,
	Jiang Liu, Tony Luck, Huang Ying, Bob Moore, Len Brown,
	Srivatsa S . Bhat, Bjorn Helgaas, linux-kernel, linux-acpi,
	linux-pci

On 10/08/2012 05:47 AM, Rafael J. Wysocki wrote:
> Hi,
> 
> On Saturday 06 of October 2012 23:27:08 Jiang Liu wrote:
>> The patchset is based on "5f3d2f2e1a63679cf1c4a4210f2f1cc2f335bef6"
>> from git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
>> And you pull the changeset from git://github.com/jiangliu/linux.git acpihp
>>
>> Modern high-end server may support advanced hotplug features for system
>> devices, including physical processor, memory board, IO extension board
>> and/or computer node. The ACPI specifications have provided standard
>> interfaces between firmware and OS to support device hotplug at runtime.
>> This patch series provide an ACPI based hotplug framework to support system
>> device hotplug at runtime, which will replace current existing ACPI device
>> driver based CPU/memory/CONTAINER hotplug mechanism.
>>
>> The new ACPI system device hotplug framework has following design goals:
>> 1) Provide a mechanism to detect hotplug slots by checking ACPI _EJ0 method,
>> ACPI PRCT (platform RAS capabilities table) and other platform specific
>> mechanisms.
> 
> Which of the patches does that?
Hi Rafael,
	Thanks for your review:)
Currently we have only implemented slot detection based on _EJ0 method. PRCT is
still in plan because that spec is not in public domain yet, ACPI5.0 only reserves
that key word without any detail information about it:(
	The patch 1-4 implement the slot enumeration driver.

> 
>> 2) Unify the way to enumerate ACPI based hotplug slots. All hotplug slots
>> will be enumerated by the enumeration driver
> 
> What is the enumeration driver?
The enumerator is a driver to create ACPI system device hotplug slots, which
takes following responsibilities:
1) walk the ACPI namespace to scan for hotpug slot with platform specific knowledge.
2) create hotplug slot device for each hotplug slot found, later a hotplug driver
will bind to those hotplug slots.
3) Provide platform specific callback methods to control those hotplug slots,
such as get slot capabilities, power on slot, power off slot etc.

> 
>> , instead of by ACPI device drivers.
>> 3) Unify the way to handle ACPI hotplug events. All ACPI hotplug events
>> for system devices will be handled by a generic ACPI hotplug driver,
>> instead of handled by ACPI device drivers.
> 
> What are "system devices"
System devices includes CPU, memory, PCI host bridge and node (ACPI module device
contains CPU, memory and/or PCI host bridge).

> 
>> 4) Solve dependencies among hotplug slots. You need first to remove the
>> memory device before removing a physical processor if a hotpluggable memory
>> device is connected to a hotpluggable physical processor.
>> 5) Provide better error handling and recover.
>> 6) Provide interface to cancel ongoing hotplug operations. It may take a
>> very long time to remove a memory device, so provide interface to cancel
>> the operation. This is important especially for memory hot-removal.
>> 7) Support new RAS features, such as socket/memory migration.
>> 8) Provide better user interfaces to the hotplug functionalities.
>> 9) Trigger hotplug events/operations by software. This feature is useful
>>    for hardware fault management and power saving.
> 
> Well, this may or may not be OK.  I'm going to review the patches, but I can't
> really say when it's going to happen (not really soon for sure).
> 

>>  create mode 100644 drivers/acpi/hotplug/slot_fake.c
>>  create mode 100644 drivers/acpi/hotplug/state_machine.c
>>  create mode 100644 drivers/acpi/hotplug/sysfs.c
>>  create mode 100644 include/acpi/acpi_hotplug.h
>>  delete mode 100644 include/acpi/container.h
> 
> And I seriously think this is too much material for a single patchset.
> 
> Please consider splitting it into smaller and well-documented series of
> patches, so that the reviewers can understand things more easily.
Thanks for your advice, I will split them into small groups next time.
The patch set could be divided into four parts:
patch 1-4: implement a ACPI system device hotplug slot enumerator
patch 6-17: implement a hotplug driver to manage all ACPI system device hotplug slots
patch 18-19: refine ACPI container driver to support new hotplug framework
patch 20-28: refine ACPI processor driver to support new hotplug framewor, also with some code cleanup

Thanks
Gerry


^ permalink raw reply	[flat|nested] 31+ messages in thread

end of thread, other threads:[~2012-10-08 15:48 UTC | newest]

Thread overview: 31+ messages (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2012-10-06 15:27 [RFC PATCH v3 00/28] ACPI based system device hotplug framework Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 01/28] ACPIHP: introduce a framework for ACPI based system device hotplug Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 02/28] ACPIHP: ACPI system device hotplug slot enumerator driver Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 03/28] ACPIHP: detect ACPI hotplug slots by checking ACPI _EJ0 method Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 04/28] ACPIHP: implement a fake ACPI system device hotplug slot enumerator Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 05/28] ACPI: introduce interfaces to manage ACPI device reference count Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 06/28] ACPIHP: introduce interfaces to scan and walk ACPI devices connecting to a slot Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 07/28] ACPIHP: use klist to manage " Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 08/28] ACPIHP: add callbacks into acpi_device_ops to support new hotplug framework Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 09/28] ACPIHP: provide interfaces to associate driver specific data with slots Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 10/28] ACPIHP: implement utility functions to support system device hotplug Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 11/28] ACPIHP: implement skeleton of ACPI system device hotplug driver Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 12/28] ACPIHP: analyse dependencies among ACPI hotplug slots Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 13/28] ACPIHP: provide interface to cancel inprogress hotplug operations Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 14/28] ACPIHP: configure/unconfigure system devices connecting to a hotplug slot Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 15/28] ACPIHP: implement the state machine for ACPI hotplug slots Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 16/28] ACPIHP: implement sysfs interfaces for ACPI system device hotplug Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 17/28] ACPIHP: block ACPI device driver unloading when executing hotplug operations Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 18/28] ACPIHP/container: change ACPI container driver to support new hotplug framework Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 19/28] ACPIHP/container: move container.c into drivers/acpi/hotplug Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 20/28] ACPI/processor: remove dead code from processor_driver.c Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 21/28] ACPIHP/processor: reorganize ACPI processor driver for new hotplug framework Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 22/28] ACPIHP/processor: protect accesses to device->driver_data Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 23/28] ACPIHP/processor: enhance processor driver to support new hotplug framework Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 24/28] CPU: introduce busy flag to temporarily disable CPU online sysfs interface Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 25/28] ACPIHP/processor: reject online/offline requests when doing processor hotplug Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 26/28] ACPI/processor: save parsed APIC ID in processor driver data structure Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 27/28] x86: simplify _acpi_map_lsapic() Jiang Liu
2012-10-06 15:27 ` [RFC PATCH v3 28/28] ACPI/processor: serialize call to acpi_map/unmap_lsapic Jiang Liu
2012-10-07 21:47 ` [RFC PATCH v3 00/28] ACPI based system device hotplug framework Rafael J. Wysocki
2012-10-08 15:45   ` Jiang Liu

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).