On Sat, 28 Jul 2012 19:42:51 +0800
Jiang Liu <liu...@gmail.com> 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 <liu...@gmail.com>
> Signed-off-by: Gaohuai Han <hangaoh...@huawei.com>
> ---
>  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....@huawei.com>
> + * Copyright (C) 2011 Gaohuai Han <hangaoh...@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 <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....@huawei.com>");
> +MODULE_AUTHOR("Gaohuai Han <hangaoh...@huawei.com>");
> -- 
> 1.7.9.5
> 
> --
> To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
> the body of a message to majord...@vger.kernel.org
> More majordomo info at  http://vger.kernel.org/majordomo-info.html
> Please read the FAQ at  http://www.tux.org/lkml/
> 


-- 
Taku Izumi <izumi.t...@jp.fujitsu.com>

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majord...@vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Reply via email to