Re: [RFC PATCH 2/3] ACPIHP: ACPI system device hotplug slot enumerator

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



On Sat, 28 Jul 2012 19:42:51 +0800
Jiang Liu <liuj97@xxxxxxxxx> wrote:

> The first is an ACPI hotplug slot enumerator, which enumerates ACPI hotplug
> slots on load and provides callbacks to manage those hotplug slots.
> An ACPI hotplug slot is an abstraction of receptacles, where a group of
> system devices could be connected to. This patch implements the skeleton for
> ACPI system device hotplug slot enumerator. On loading, the driver scans the
> whole ACPI namespace for hotplug slots and creates a device node for each
> hotplug slots. Every slot is associated with a device class named
> acpihp_slot_class and will be managed by ACPI hotplug drivers.
> 
> The hotplug enumerator will create following sysfs entries for hotplug slots:
> 
> linux-drf:/sys/devices/LNXSYSTM:00/acpihp # ll
> drwxr-xr-x 4 root root 0 Jul 28 16:00 NODE00
> drwxr-xr-x 3 root root 0 Jul 28 16:00 NODE01
> drwxr-xr-x 3 root root 0 Jul 28 16:00 NODE02
> 
> linux-drf:/sys/devices/LNXSYSTM:00/acpihp/NODE00 # ll
> drwxr-xr-x 3 root root     0 Jul 28 16:00 IOX01
> -r--r--r-- 1 root root 65536 Jul 28 16:01 capabilities
> lrwxrwxrwx 1 root root     0 Jul 28 16:00 device -> ../../../LNXSYSTM:00
> -r--r--r-- 1 root root 65536 Jul 28 16:01 object
> drwxr-xr-x 2 root root     0 Jul 28 16:01 power
> -r--r--r-- 1 root root 65536 Jul 28 16:01 state
> -r--r--r-- 1 root root 65536 Jul 28 16:01 status
> lrwxrwxrwx 1 root root     0 Jul 28 16:00 subsystem -> ../../../../class/acpihp
> -r--r--r-- 1 root root 65536 Jul 28 16:01 type
> -rw-r--r-- 1 root root 65536 Jul 28 16:01 uevent
> 
> linux-drf:/sys/bus/acpi/acpihp # ls
> NODE00  NODE00.IOX01  NODE01  NODE02
> 
> linux-drf:/sys/bus/acpi/acpihp # ll
> lrwxrwxrwx 1 root root 0 Jul 28 16:03 NODE00 ->
> 		../../../devices/LNXSYSTM:00/acpihp/NODE00
> lrwxrwxrwx 1 root root 0 Jul 28 16:03 NODE00.IOX01 ->
> 		../../../devices/LNXSYSTM:00/acpihp/NODE00/IOX01
> lrwxrwxrwx 1 root root 0 Jul 28 16:03 NODE01 ->
> 		../../../devices/LNXSYSTM:00/acpihp/NODE01
> lrwxrwxrwx 1 root root 0 Jul 28 16:03 NODE02 ->
> 		../../../devices/LNXSYSTM:00/acpihp/NODE02
> 
> Signed-off-by: Jiang Liu <liuj97@xxxxxxxxx>
> Signed-off-by: Gaohuai Han <hangaohuai@xxxxxxxxxx>
> ---
>  drivers/acpi/Kconfig             |   11 +
>  drivers/acpi/hotplug/Makefile    |    3 +
>  drivers/acpi/hotplug/slot_enum.c |  466 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 480 insertions(+)
>  create mode 100644 drivers/acpi/hotplug/slot_enum.c
> 
> diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig
> index e457d31..711e18e 100644
> --- a/drivers/acpi/Kconfig
> +++ b/drivers/acpi/Kconfig
> @@ -333,6 +333,17 @@ menuconfig ACPI_HOTPLUG
>  	  If your hardware and firmware do not support adding or removing
>  	  of system devices at runtime, you need not to enable this option.
>  
> +config ACPI_HOTPLUG_ENUM
> +	tristate "ACPI Hotplug Slot Enumerator"
> +	depends on ACPI_HOTPLUG
> +	default y
> +	help
> +	  This driver enumerates ACPI hotplug slots for ACPI based system
> +	  device hotplug.
> +
> +	  To compile this driver as a module, choose M here:
> +	  the module will be called acpihp_enum.
> +
>  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..41c0da9 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_ENUM)			+= acpihp_enum.o
> +acpihp_enum-y					= slot_enum.o
> diff --git a/drivers/acpi/hotplug/slot_enum.c b/drivers/acpi/hotplug/slot_enum.c
> new file mode 100644
> index 0000000..80396a3
> --- /dev/null
> +++ b/drivers/acpi/hotplug/slot_enum.c
> @@ -0,0 +1,466 @@
> +/*
> + * Copyright (C) 2011 Huawei Tech. Co., Ltd.
> + * Copyright (C) 2011 Jiang Liu <jiang.liu@xxxxxxxxxx>
> + * Copyright (C) 2011 Gaohuai Han <hangaohuai@xxxxxxxxxx>
> + *
> + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
> + *
> + * 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 <acpi/acpi.h>
> +#include <acpi/acpi_hotplug.h>
> +
> +static LIST_HEAD(slot_list);
> +static LIST_HEAD(slot_id_list);
> +
> +struct acpihp_slot_id {
> +	struct list_head node;
> +	unsigned long instance_id;
> +	enum acpihp_slot_type type;
> +};
> +
> +static struct acpihp_slot_ops *slot_ops_curr;
> +
> +/*
> + * Array of platform specific enumeration methods.
> + * Entries in the array should be sorted by descending priority order.
> + */
> +static struct acpihp_slot_ops *slot_ops_array[] = {
> +	NULL
> +};
> +
> +static void acpihp_enum_cleanup_slots(void);
> +
> +static int __init acpihp_get_parent_slot(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_WARN("fails to get ACPI root device.\n");
> +		return -EINVAL;
> +	}
> +
> +	do {
> +		if (ACPI_FAILURE(acpi_get_parent(handle, &handle))) {
> +			ACPIHP_DEBUG("fails to get parent device handle.\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_get_slot_state(struct acpihp_slot *slot)
> +{
> +	unsigned long long sta;
> +
> +	/* An hotplug slot must implement _STA method. */
> +	if (ACPI_FAILURE(acpi_evaluate_integer(slot->handle, METHOD_NAME__STA,
> +					       NULL, &sta))) {
> +		ACPIHP_DEBUG("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_enum_create_slot(acpi_handle handle)
> +{
> +	struct acpihp_slot *slot;
> +
> +	slot = acpihp_create_slot(handle, "TEMP");
> +	if (!slot) {
> +		ACPIHP_DEBUG("fails to allocate memory for hotplug slot.\n");
> +		return -ENOMEM;
> +	}
> +
> +	slot->slot_ops = slot_ops_curr;
> +
> +	if (acpihp_get_parent_slot(slot))
> +		goto out;
> +	if (acpihp_get_slot_state(slot))
> +		goto out;
> +	if (ACPI_FAILURE(acpihp_slot_get_capabilities(slot,
> +						      &slot->capabilities))) {
> +		ACPIHP_DEBUG("fails to get slot capabilities.\n");
> +		goto out;
> +	}
> +	if (ACPI_FAILURE(acpihp_mark_slot(handle, slot))) {
> +		ACPIHP_DEBUG("fails to attach slot to ACPI device object.\n");
> +		goto out;
> +	}
> +
> +	list_add_tail(&slot->slot_list, &slot_list);
> +
> +	return 0;
> +out:
> +	acpihp_slot_put(slot);
> +	return -EINVAL;
> +}
> +
> +/*
> + * Scan hotplug slots for ACPI based system device hotplug.
> + * We only care about processor, memory, PCI host bridge and CONTAINER.
> + */
> +static acpi_status __init acpihp_enum_scan_slot(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_enum_create_slot(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;
> +}
> +
> +/*
> + * Get types of child devices connected to this slot.
> + * We only care about CPU, memory, PCI host bridge and CONTAINER here.
> + * Values used here must be in consistence with acpihp_enum_get_slot_type().
> + */
> +static acpi_status __init
> +acpihp_enum_get_dev_type(acpi_handle handle, u32 lvl, void *context, void **rv)
> +{
> +	acpi_status status = AE_OK;
> +	enum acpihp_dev_type type;
> +	u32 *tp = (u32 *)rv;
> +
> +	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;
> +}
> +
> +/*
> + * Guess type of a hotplug slot according to child devices connecting to it.
> + */
> +static enum acpihp_slot_type __init acpihp_enum_get_slot_type(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;


   Why is this case ACPIHP_SLOT_TYPE_CPU? 
   I think this case is ACPIHP_SLOT_TYPE_COMMON or else.
   By the way how about simplifying slot type category?
   Do we need to differentiate case7, 8, 9, 11 and 15?
	 
   Best regards,
   Taku Izumi


> +	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;
> +	}
> +}
> +
> +/*
> + * Guess type of a hotplug slot according to the device type of the
> + * corresponding ACPI object itself.
> + */
> +static enum acpihp_slot_type __init
> +acpihp_enum_check_slot_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;
> +	}
> +}
> +
> +static int __init acpihp_enum_generate_slot_name(struct acpihp_slot *slot)
> +{
> +	int found = 0;
> +	struct list_head *list;
> +	struct acpihp_slot_id *slot_id;
> +	unsigned long long uid;
> +
> +	/* Respect firmware settings if _UID return an integer. */
> +	if (ACPI_SUCCESS(acpi_evaluate_integer(slot->handle, METHOD_NAME__UID,
> +					       NULL, &uid)))
> +		goto set_name;
> +
> +	if (slot->parent)
> +		list = &slot->parent->slot_id_list;
> +	else
> +		list = &slot_id_list;
> +
> +	list_for_each_entry(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_DEBUG("fails to allocate slot instance ID.\n");
> +			return -ENOMEM;
> +		}
> +		slot_id->type = slot->type;
> +		list_add_tail(&slot_id->node, 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;
> +}
> +
> +/*
> + * Generate a meaningful name for the slot according to devices connecting
> + * to this slot
> + */
> +static int __init acpihp_enum_rename_slot(struct acpihp_slot *slot)
> +{
> +	u32 child_types = 0;
> +
> +	slot->type = acpihp_enum_check_slot_self(slot);
> +	if (slot->type == ACPIHP_SLOT_TYPE_UNKNOWN) {
> +		acpi_walk_namespace(ACPI_TYPE_DEVICE, slot->handle,
> +				ACPI_UINT32_MAX, acpihp_enum_get_dev_type,
> +				NULL, NULL, (void **)&child_types);
> +		acpi_walk_namespace(ACPI_TYPE_PROCESSOR, slot->handle,
> +				ACPI_UINT32_MAX, acpihp_enum_get_dev_type,
> +				NULL, NULL, (void **)&child_types);
> +		slot->type = acpihp_enum_get_slot_type(child_types);
> +	}
> +
> +	if (acpihp_enum_generate_slot_name(slot))
> +		return -EINVAL;
> +
> +	return 0;
> +}
> +
> +static void __init acpihp_enum_rename_and_register_slots(void)
> +{
> +	struct acpihp_slot *slot;
> +
> +	list_for_each_entry(slot, &slot_list, slot_list) {
> +		/* generate a meaningful name for this slot */
> +		if (acpihp_enum_rename_slot(slot))
> +			continue;
> +
> +		if (acpihp_register_slot(slot))
> +			ACPIHP_DEBUG("fails to register slot %s.\n",
> +				     slot->name);
> +	}
> +}
> +
> +static int __init acpihp_enum_generate_slots(void)
> +{
> +	acpi_status status;
> +
> +	status = acpi_walk_namespace(ACPI_TYPE_DEVICE, ACPI_ROOT_OBJECT,
> +				     ACPI_UINT32_MAX, acpihp_enum_scan_slot,
> +				     NULL, NULL, NULL);
> +	if (!ACPI_SUCCESS(status))
> +		goto out_err;
> +
> +	status = acpi_walk_namespace(ACPI_TYPE_PROCESSOR, ACPI_ROOT_OBJECT,
> +				     ACPI_UINT32_MAX, acpihp_enum_scan_slot,
> +				     NULL, NULL, NULL);
> +	if (!ACPI_SUCCESS(status))
> +		goto out_err;
> +
> +	acpihp_enum_rename_and_register_slots();
> +
> +	return 0;
> +
> +out_err:
> +	ACPIHP_DEBUG("fails to scan hotplug slots.\n");
> +	acpihp_enum_cleanup_slots();
> +
> +	return -ENOTSUPP;
> +}
> +
> +static void acpihp_enum_unregister_slots(void)
> +{
> +	struct acpihp_slot *slot, *tmp;
> +	struct acpihp_slot_id *slot_id, *slot_id_safe;
> +
> +	list_for_each_entry_safe(slot, tmp, &slot_list, slot_list) {
> +		acpihp_unregister_slot(slot);
> +		list_del_init(&slot->slot_list);
> +		acpihp_unmark_slot(slot->handle);
> +		list_for_each_entry_safe(slot_id, slot_id_safe,
> +					 &slot->slot_id_list, node) {
> +			list_del(&slot_id->node);
> +			kfree(slot_id);
> +		}
> +		acpihp_slot_put(slot);
> +	}
> +}
> +
> +static void acpihp_enum_cleanup_slots(void)
> +{
> +	struct acpihp_slot_id *slot_id, *tmp;
> +
> +	acpihp_enum_unregister_slots();
> +	list_for_each_entry_safe(slot_id, tmp, &slot_id_list, node) {
> +		list_del(&slot_id->node);
> +		kfree(slot_id);
> +	}
> +}
> +
> +static int __init acpihp_enum_init(void)
> +{
> +	int i;
> +	int retval;
> +
> +	/* probe for suitable enumerator. */
> +	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 ACPI hotplug slot found.\n");
> +		return -ENXIO;
> +	}
> +
> +	retval = acpihp_register_class();
> +	if (retval != 0) {
> +		ACPIHP_DEBUG("fails to register ACPI hotplug slot class.\n");
> +		goto out_fini;
> +	}
> +
> +	retval = acpihp_enum_generate_slots();
> +	if (retval != 0) {
> +		ACPIHP_DEBUG("fails to enumerate ACPI hotplug slots.\n");
> +		goto out_unregister_class;
> +	}
> +
> +	/* Back out if no ACPI hotplug slot  found. */
> +	if (list_empty(&slot_list)) {
> +		ACPIHP_DEBUG("no ACPI hotplug slot found.\n");
> +		retval = -ENODEV;
> +		goto out_unregister_class;
> +	}
> +
> +	return 0;
> +
> +out_unregister_class:
> +	acpihp_unregister_class();
> +out_fini:
> +	slot_ops_curr->fini();
> +	ACPIHP_DEBUG("fails to initialize hotplug slot enumerator.\n");
> +
> +	return retval;
> +}
> +
> +static void __exit acpihp_enum_exit(void)
> +{
> +	acpihp_enum_cleanup_slots();
> +	acpihp_unregister_class();
> +	slot_ops_curr->fini();
> +}
> +
> +module_init(acpihp_enum_init);
> +module_exit(acpihp_enum_exit);
> +
> +MODULE_LICENSE("GPL v2");
> +MODULE_AUTHOR("Jiang Liu <jiang.liu@xxxxxxxxxx>");
> +MODULE_AUTHOR("Gaohuai Han <hangaohuai@xxxxxxxxxx>");
> -- 
> 1.7.9.5
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majordomo@xxxxxxxxxxxxxxx
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
> 


-- 
Taku Izumi <izumi.taku@xxxxxxxxxxxxxx>

--
To unsubscribe from this list: send the line "unsubscribe linux-acpi" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html


[Index of Archives]     [Linux IBM ACPI]     [Linux Power Management]     [Linux Kernel]     [Linux Laptop]     [Kernel Newbies]     [Share Photos]     [Security]     [Netfilter]     [Bugtraq]     [Yosemite News]     [MIPS Linux]     [ARM Linux]     [Linux Security]     [Linux RAID]     [Samba]     [Video 4 Linux]     [Device Mapper]     [Linux Resources]

  Powered by Linux