Message ID | 1344082443-4608-11-git-send-email-jiang.liu@huawei.com (mailing list archive) |
---|---|
State | New, archived |
Delegated to: | Bjorn Helgaas |
Headers | show |
Hi Liu~ I compiled this driver as a module, acpihp_drv. And when I loaded this module, it gave the following error message: # modprobe acpihp_drv (the command hangs up, no return after 10 min) #dmesg ...... [ 126.643350] BUG: unable to handle kernel NULL pointer dereference at 0000000000000078 [ 126.644007] IP: [<ffffffff814c0cd3>] mutex_lock+0x19/0x37 [ 126.644007] PGD 105277a067 PUD 104f823067 PMD 0 [ 126.644007] Oops: 0002 [#1] SMP [ 126.644007] Modules linked in: acpihp_drv(+) ebtable_nat ebtables ipt_MASQUERADE iptable_nat nf_nat iptable_mangle bridge stp llc sunrpc cpufreq_ondemand acpi_cpufreq freq_table mperf ip6t_REJECT nf_conntrack_ipv6 nf_defrag_ipv6 ip6table_filter ip6_tables ipv6 vhost_net macvtap macvlan tun uinput iTCO_wdt iTCO_vendor_support coretemp kvm_intel kvm crc32c_intel microcode lpc_ich mfd_core pcspkr i2c_i801 i2c_core ioatdma e1000e acpi_memhotplug i7core_edac edac_core igb dca mptsas mptscsih mptbase scsi_transport_sas [ 126.644007] CPU 10 [ 126.644007] Pid: 2821, comm: modprobe Tainted: G A 3.6.0-rc1+ #6 FUJITSU-SV PRIMEQUEST 1800E/SB [ 126.644007] RIP: 0010:[<ffffffff814c0cd3>] [<ffffffff814c0cd3>] mutex_lock+0x19/0x37 [ 126.644007] RSP: 0018:ffff8810589a9de8 EFLAGS: 00010246 [ 126.644007] RAX: 0000000000000000 RBX: 0000000000000078 RCX: 0000000000000000 [ 126.644007] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000078 [ 126.644007] RBP: ffff8810589a9e08 R08: 0000000000000000 R09: ffff8810589a9d88 [ 126.644007] R10: 00000000000013e5 R11: 00000000000013e5 R12: ffffffffa01460d0 [ 126.644007] R13: 0000000000000000 R14: ffffffffa014732b R15: 00000000000000bf [ 126.644007] FS: 00007fecb1802700(0000) GS:ffff88105e640000(0000) knlGS:0000000000000000 [ 126.644007] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 [ 126.644007] CR2: 0000000000000078 CR3: 0000001052772000 CR4: 00000000000007e0 [ 126.644007] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 [ 126.644007] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 [ 126.644007] Process modprobe (pid: 2821, threadinfo ffff8810589a8000, task ffff8810592f8000) [ 126.644007] Stack: [ 126.644007] ffff8810589a9e08 ffffffff810be37f ffffffffa0146220 ffffffff81a7b390 [ 126.644007] ffff8810589a9e58 ffffffff81317eb3 ffff8810589a9e48 0000000000000000 [ 126.644007] ffffffff81a342c0 ffffffff81a342e0 0000000000000000 ffffffffa0146220 [ 126.644007] Call Trace: [ 126.644007] [<ffffffff810be37f>] ? tracepoint_module_notify+0xd9/0x14a [ 126.644007] [<ffffffff81317eb3>] class_interface_register+0x4a/0xbc [ 126.644007] [<ffffffffa00b8000>] ? 0xffffffffa00b7fff [ 126.644007] [<ffffffffa00b8010>] acpihp_drv_init+0x10/0x12 [acpihp_drv] [ 126.644007] [<ffffffff8100207f>] do_one_initcall+0x7f/0x139 [ 126.644007] [<ffffffff81093414>] sys_init_module+0x12d3/0x14e3 [ 126.644007] [<ffffffff81264b0d>] ? ddebug_dyndbg_boot_param_cb+0x45/0x45 [ 126.644007] [<ffffffff814c9469>] system_call_fastpath+0x16/0x1b [ 126.644007] Code: 48 8b 04 25 80 c6 00 00 48 89 43 18 31 c0 5b 5b c9 c3 55 48 89 e5 53 48 83 ec 18 66 66 66 66 90 48 89 fb e8 5a 0c 00 00 48 89 df <f0> ff 0f 79 05 e8 06 ff ff ff 65 48 8b 04 25 80 c6 00 00 48 89 [ 126.644007] RIP [<ffffffff814c0cd3>] mutex_lock+0x19/0x37 [ 126.644007] RSP <ffff8810589a9de8> [ 126.644007] CR2: 0000000000000078 [ 129.981335] ---[ end trace da17e9c9de8dd560 ]--- [ 139.085895] nr_pdflush_threads exported in /proc is scheduled for removal [ 139.167394] sysctl: The scan_unevictable_pages sysctl/node-interface has been disabled for lack of a legitimate use case. If you have one, please send an email to linux-mm@kvack.org. Looks like it dereferenced a NULL pointer here. May be it was my mistake that I didn't configure the environment correctly. Would you please give me some advice ? Thanks. :) On 08/04/2012 08:13 PM, Jiang Liu wrote: > From: Jiang Liu <jiang.liu@huawei.com> > > This patch implements a skeleton for ACPI based system device hotplug driver. > This device class driver will be bound to and manage ACPI hotplug slots. > > This is the default hotplug driver for ACPI based system device hotplug. > > Signed-off-by: Jiang Liu <liuj97@gmail.com> > Signed-off-by: Hanjun Guo <guohanjun@huawei.com> > --- > drivers/acpi/Kconfig | 12 ++ > drivers/acpi/hotplug/Makefile | 3 + > drivers/acpi/hotplug/acpihp_drv.h | 62 +++++++ > drivers/acpi/hotplug/drv_main.c | 331 +++++++++++++++++++++++++++++++++++++ > 4 files changed, 408 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 c9f7918..89047a3 100644 > --- a/drivers/acpi/Kconfig > +++ b/drivers/acpi/Kconfig > @@ -354,6 +354,18 @@ config ACPI_HOTPLUG_ENUM_EJ0 > > It's the default method to detect ACPI hotplug slots. > > +config ACPI_HOTPLUG_DRIVER > + tristate "ACPI Based System Device Hotplug Driver" > + depends on ACPI_HOTPLUG > + default y > + help > + This driver enables ACPI based system device hotplug, including > + physical processor, memory device, IO host bridge and computer > + node etc. > + > + 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 25fac24..d69832f 100644 > --- a/drivers/acpi/hotplug/Makefile > +++ b/drivers/acpi/hotplug/Makefile > @@ -8,3 +8,6 @@ acpihp-y = core.o device.o > obj-$(CONFIG_ACPI_HOTPLUG_ENUM) += acpihp_enum.o > acpihp_enum-y = slot_enum.o > acpihp_enum-y += slot_enum_ej0.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..18330f7 > --- /dev/null > +++ b/drivers/acpi/hotplug/acpihp_drv.h > @@ -0,0 +1,62 @@ > +/* > + * Copyright (C) 2011 Huawei Tech. Co., Ltd. > + * Copyright (C) 2011 Jiang Liu <jiang.liu@huawei.com> > + * Copyright (C) 2011 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_status; > + atomic_t cancel_flag; > + 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..538772d > --- /dev/null > +++ b/drivers/acpi/hotplug/drv_main.c > @@ -0,0 +1,331 @@ > +/* > + * Copyright (C) 2011 Huawei Tech. Co., Ltd. > + * Copyright (C) 2011 Jiang Liu <jiang.liu@huawei.com> > + * Copyright (C) 2011 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 state 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_MEM; type <= ACPIHP_DEV_TYPE_HOST_BRIDGE; > + 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_DEBUG("fails to get info about %s.\n", > + dev_name(&dev->dev)); > + 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); > + } > + } > + > +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_OK; > + 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_DEBUG("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_DEBUG("fails to add %s to slot %s.\n", > + dev_name(&dev->dev), slot->name); > + rv = AE_ERROR; > + } > + } else { > + ACPIHP_DEBUG("fails to query device info of %s.\n", > + dev_name(&dev->dev)); > + } > + > + 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_DEBUG("%s to install event handler for %s.\n", > + ACPI_SUCCESS(status) ? "succeeds" : "fails", slot->name); > + > + 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_DEBUG("%s to uninstall event handler for %s.\n", > + ACPI_SUCCESS(status) ? "succeeds" : "fails", slot->name); > +} > + > +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_DEBUG("fails to get reference to slot_ops for %s.\n", > + slot->name); > + return -EINVAL; > + } > + > + /* install ACPI event notification handler for slot */ > + if (ACPI_FAILURE(acpihp_drv_install_handler(slot))) { > + ACPIHP_DEBUG("fails to install event handler for %s.\n", > + slot->name); > + module_put(slot->slot_ops->owner); > + return -EBUSY; > + } > + > + /* Enumerate all devices if slot is already powered. */ > + if (!acpihp_slot_powered(slot)) > + ACPIHP_DEBUG("slot %s is powered off.\n", slot->name); > + 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_DEBUG("fails to attach driver data for %s.\n", > + slot->name); > + acpihp_drv_remove_devices(slot); > + module_put(slot->slot_ops->owner); > + kfree(drv_data); > + return -ENOMEM; > + } > + > + ACPIHP_INFO("found hotplug slot %s.\n", slot->full_path); > + > + 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_INFO("remove hotplug slot %s.\n", slot->full_path); > + > + 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) > +{ > + return class_interface_register(&acpihp_drv_interface); > +} > + > +static void __exit acpihp_drv_exit(void) > +{ > + class_interface_unregister(&acpihp_drv_interface); > +} > + > +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>");
Hi Tang, Thanks for testing. Currently there's a limitation that you need to insert acpihp_enum driver first. Will fix this issue in next version. Regards! Gerry On 2012-8-9 15:12, Tang Chen wrote: > Hi Liu~ > > I compiled this driver as a module, acpihp_drv. And when I loaded this module, it > gave the following error message: > > # modprobe acpihp_drv > (the command hangs up, no return after 10 min) > > #dmesg > ...... > [ 126.643350] BUG: unable to handle kernel NULL pointer dereference at 0000000000000078 > [ 126.644007] IP: [<ffffffff814c0cd3>] mutex_lock+0x19/0x37 > [ 126.644007] PGD 105277a067 PUD 104f823067 PMD 0 > [ 126.644007] Oops: 0002 [#1] SMP > [ 126.644007] Modules linked in: acpihp_drv(+) ebtable_nat ebtables ipt_MASQUERADE iptable_nat nf_nat iptable_mangle bridge stp llc sunrpc cpufreq_ondemand acpi_cpufreq freq_table mperf ip6t_REJECT nf_conntrack_ipv6 nf_defrag_ipv6 ip6table_filter ip6_tables ipv6 vhost_net macvtap macvlan tun uinput iTCO_wdt iTCO_vendor_support coretemp kvm_intel kvm crc32c_intel microcode lpc_ich mfd_core pcspkr i2c_i801 i2c_core ioatdma e1000e acpi_memhotplug i7core_edac edac_core igb dca mptsas mptscsih mptbase scsi_transport_sas > [ 126.644007] CPU 10 > [ 126.644007] Pid: 2821, comm: modprobe Tainted: G A 3.6.0-rc1+ #6 FUJITSU-SV PRIMEQUEST 1800E/SB > [ 126.644007] RIP: 0010:[<ffffffff814c0cd3>] [<ffffffff814c0cd3>] mutex_lock+0x19/0x37 > [ 126.644007] RSP: 0018:ffff8810589a9de8 EFLAGS: 00010246 > [ 126.644007] RAX: 0000000000000000 RBX: 0000000000000078 RCX: 0000000000000000 > [ 126.644007] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000078 > [ 126.644007] RBP: ffff8810589a9e08 R08: 0000000000000000 R09: ffff8810589a9d88 > [ 126.644007] R10: 00000000000013e5 R11: 00000000000013e5 R12: ffffffffa01460d0 > [ 126.644007] R13: 0000000000000000 R14: ffffffffa014732b R15: 00000000000000bf > [ 126.644007] FS: 00007fecb1802700(0000) GS:ffff88105e640000(0000) knlGS:0000000000000000 > [ 126.644007] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 > [ 126.644007] CR2: 0000000000000078 CR3: 0000001052772000 CR4: 00000000000007e0 > [ 126.644007] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 > [ 126.644007] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 > [ 126.644007] Process modprobe (pid: 2821, threadinfo ffff8810589a8000, task ffff8810592f8000) > [ 126.644007] Stack: > [ 126.644007] ffff8810589a9e08 ffffffff810be37f ffffffffa0146220 ffffffff81a7b390 > [ 126.644007] ffff8810589a9e58 ffffffff81317eb3 ffff8810589a9e48 0000000000000000 > [ 126.644007] ffffffff81a342c0 ffffffff81a342e0 0000000000000000 ffffffffa0146220 > [ 126.644007] Call Trace: > [ 126.644007] [<ffffffff810be37f>] ? tracepoint_module_notify+0xd9/0x14a > [ 126.644007] [<ffffffff81317eb3>] class_interface_register+0x4a/0xbc > [ 126.644007] [<ffffffffa00b8000>] ? 0xffffffffa00b7fff > [ 126.644007] [<ffffffffa00b8010>] acpihp_drv_init+0x10/0x12 [acpihp_drv] > [ 126.644007] [<ffffffff8100207f>] do_one_initcall+0x7f/0x139 > [ 126.644007] [<ffffffff81093414>] sys_init_module+0x12d3/0x14e3 > [ 126.644007] [<ffffffff81264b0d>] ? ddebug_dyndbg_boot_param_cb+0x45/0x45 > [ 126.644007] [<ffffffff814c9469>] system_call_fastpath+0x16/0x1b > [ 126.644007] Code: 48 8b 04 25 80 c6 00 00 48 89 43 18 31 c0 5b 5b c9 c3 55 48 89 e5 53 48 83 ec 18 66 66 66 66 90 48 89 fb e8 5a 0c 00 00 48 89 df <f0> ff 0f 79 05 e8 06 ff ff ff 65 48 8b 04 25 80 c6 00 00 48 89 > [ 126.644007] RIP [<ffffffff814c0cd3>] mutex_lock+0x19/0x37 > [ 126.644007] RSP <ffff8810589a9de8> > [ 126.644007] CR2: 0000000000000078 > [ 129.981335] ---[ end trace da17e9c9de8dd560 ]--- > [ 139.085895] nr_pdflush_threads exported in /proc is scheduled for removal > [ 139.167394] sysctl: The scan_unevictable_pages sysctl/node-interface has been disabled for lack of a legitimate use case. If you have one, please send an email to linux-mm@kvack.org. > > Looks like it dereferenced a NULL pointer here. > May be it was my mistake that I didn't configure the environment correctly. > Would you please give me some advice ? > > Thanks. :) > > > On 08/04/2012 08:13 PM, Jiang Liu wrote: >> From: Jiang Liu <jiang.liu@huawei.com> >> >> This patch implements a skeleton for ACPI based system device hotplug driver. >> This device class driver will be bound to and manage ACPI hotplug slots. >> >> This is the default hotplug driver for ACPI based system device hotplug. >> >> Signed-off-by: Jiang Liu <liuj97@gmail.com> >> Signed-off-by: Hanjun Guo <guohanjun@huawei.com> >> --- >> drivers/acpi/Kconfig | 12 ++ >> drivers/acpi/hotplug/Makefile | 3 + >> drivers/acpi/hotplug/acpihp_drv.h | 62 +++++++ >> drivers/acpi/hotplug/drv_main.c | 331 +++++++++++++++++++++++++++++++++++++ >> 4 files changed, 408 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 c9f7918..89047a3 100644 >> --- a/drivers/acpi/Kconfig >> +++ b/drivers/acpi/Kconfig >> @@ -354,6 +354,18 @@ config ACPI_HOTPLUG_ENUM_EJ0 >> >> It's the default method to detect ACPI hotplug slots. >> >> +config ACPI_HOTPLUG_DRIVER >> + tristate "ACPI Based System Device Hotplug Driver" >> + depends on ACPI_HOTPLUG >> + default y >> + help >> + This driver enables ACPI based system device hotplug, including >> + physical processor, memory device, IO host bridge and computer >> + node etc. >> + >> + 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 25fac24..d69832f 100644 >> --- a/drivers/acpi/hotplug/Makefile >> +++ b/drivers/acpi/hotplug/Makefile >> @@ -8,3 +8,6 @@ acpihp-y = core.o device.o >> obj-$(CONFIG_ACPI_HOTPLUG_ENUM) += acpihp_enum.o >> acpihp_enum-y = slot_enum.o >> acpihp_enum-y += slot_enum_ej0.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..18330f7 >> --- /dev/null >> +++ b/drivers/acpi/hotplug/acpihp_drv.h >> @@ -0,0 +1,62 @@ >> +/* >> + * Copyright (C) 2011 Huawei Tech. Co., Ltd. >> + * Copyright (C) 2011 Jiang Liu <jiang.liu@huawei.com> >> + * Copyright (C) 2011 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_status; >> + atomic_t cancel_flag; >> + 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..538772d >> --- /dev/null >> +++ b/drivers/acpi/hotplug/drv_main.c >> @@ -0,0 +1,331 @@ >> +/* >> + * Copyright (C) 2011 Huawei Tech. Co., Ltd. >> + * Copyright (C) 2011 Jiang Liu <jiang.liu@huawei.com> >> + * Copyright (C) 2011 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 state 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_MEM; type <= ACPIHP_DEV_TYPE_HOST_BRIDGE; >> + 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_DEBUG("fails to get info about %s.\n", >> + dev_name(&dev->dev)); >> + 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); >> + } >> + } >> + >> +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_OK; >> + 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_DEBUG("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_DEBUG("fails to add %s to slot %s.\n", >> + dev_name(&dev->dev), slot->name); >> + rv = AE_ERROR; >> + } >> + } else { >> + ACPIHP_DEBUG("fails to query device info of %s.\n", >> + dev_name(&dev->dev)); >> + } >> + >> + 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_DEBUG("%s to install event handler for %s.\n", >> + ACPI_SUCCESS(status) ? "succeeds" : "fails", slot->name); >> + >> + 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_DEBUG("%s to uninstall event handler for %s.\n", >> + ACPI_SUCCESS(status) ? "succeeds" : "fails", slot->name); >> +} >> + >> +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_DEBUG("fails to get reference to slot_ops for %s.\n", >> + slot->name); >> + return -EINVAL; >> + } >> + >> + /* install ACPI event notification handler for slot */ >> + if (ACPI_FAILURE(acpihp_drv_install_handler(slot))) { >> + ACPIHP_DEBUG("fails to install event handler for %s.\n", >> + slot->name); >> + module_put(slot->slot_ops->owner); >> + return -EBUSY; >> + } >> + >> + /* Enumerate all devices if slot is already powered. */ >> + if (!acpihp_slot_powered(slot)) >> + ACPIHP_DEBUG("slot %s is powered off.\n", slot->name); >> + 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_DEBUG("fails to attach driver data for %s.\n", >> + slot->name); >> + acpihp_drv_remove_devices(slot); >> + module_put(slot->slot_ops->owner); >> + kfree(drv_data); >> + return -ENOMEM; >> + } >> + >> + ACPIHP_INFO("found hotplug slot %s.\n", slot->full_path); >> + >> + 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_INFO("remove hotplug slot %s.\n", slot->full_path); >> + >> + 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) >> +{ >> + return class_interface_register(&acpihp_drv_interface); >> +} >> + >> +static void __exit acpihp_drv_exit(void) >> +{ >> + class_interface_unregister(&acpihp_drv_interface); >> +} >> + >> +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>"); > -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
Hi Liu ~ On 08/09/2012 03:40 PM, Jiang Liu wrote: > Hi Tang, > Thanks for testing. > Currently there's a limitation that you need to insert acpihp_enum driver first. Sorry, I didn't make it clear. I did load acpihp_enum module first, and then load acpihp_drv. And I just tried it some more times. It just hung up, but dmesg had no output. Like this: # modprobe acpihp_enum (OK, and sysfs interfaces have been created) # modprobe acpihp_drv (hang up) # dmesg (nothing) The "modprobe acpihp_drv" process's call trace shows that it hung at the following function: #0 0x00000032836aab80 in __nanosleep_nocancel () from /lib64/libc.so.6 #1 0x00000032836deb64 in usleep () from /lib64/libc.so.6 ...... I have tried several times and I cannot reproduce the situation I just said. Maybe my box has something different with yours. And I'll try to find out why. Thanks for your advice. :) > Will fix this issue in next version. > Regards! > Gerry > > On 2012-8-9 15:12, Tang Chen wrote: >> Hi Liu~ >> >> I compiled this driver as a module, acpihp_drv. And when I loaded this module, it >> gave the following error message: >> >> # modprobe acpihp_drv >> (the command hangs up, no return after 10 min) >> >> #dmesg >> ...... >> [ 126.643350] BUG: unable to handle kernel NULL pointer dereference at 0000000000000078 >> [ 126.644007] IP: [<ffffffff814c0cd3>] mutex_lock+0x19/0x37 >> [ 126.644007] PGD 105277a067 PUD 104f823067 PMD 0 >> [ 126.644007] Oops: 0002 [#1] SMP >> [ 126.644007] Modules linked in: acpihp_drv(+) ebtable_nat ebtables ipt_MASQUERADE iptable_nat nf_nat iptable_mangle bridge stp llc sunrpc cpufreq_ondemand acpi_cpufreq freq_table mperf ip6t_REJECT nf_conntrack_ipv6 nf_defrag_ipv6 ip6table_filter ip6_tables ipv6 vhost_net macvtap macvlan tun uinput iTCO_wdt iTCO_vendor_support coretemp kvm_intel kvm crc32c_intel microcode lpc_ich mfd_core pcspkr i2c_i801 i2c_core ioatdma e1000e acpi_memhotplug i7core_edac edac_core igb dca mptsas mptscsih mptbase scsi_transport_sas >> [ 126.644007] CPU 10 >> [ 126.644007] Pid: 2821, comm: modprobe Tainted: G A 3.6.0-rc1+ #6 FUJITSU-SV PRIMEQUEST 1800E/SB >> [ 126.644007] RIP: 0010:[<ffffffff814c0cd3>] [<ffffffff814c0cd3>] mutex_lock+0x19/0x37 >> [ 126.644007] RSP: 0018:ffff8810589a9de8 EFLAGS: 00010246 >> [ 126.644007] RAX: 0000000000000000 RBX: 0000000000000078 RCX: 0000000000000000 >> [ 126.644007] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000078 >> [ 126.644007] RBP: ffff8810589a9e08 R08: 0000000000000000 R09: ffff8810589a9d88 >> [ 126.644007] R10: 00000000000013e5 R11: 00000000000013e5 R12: ffffffffa01460d0 >> [ 126.644007] R13: 0000000000000000 R14: ffffffffa014732b R15: 00000000000000bf >> [ 126.644007] FS: 00007fecb1802700(0000) GS:ffff88105e640000(0000) knlGS:0000000000000000 >> [ 126.644007] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 >> [ 126.644007] CR2: 0000000000000078 CR3: 0000001052772000 CR4: 00000000000007e0 >> [ 126.644007] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 >> [ 126.644007] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 >> [ 126.644007] Process modprobe (pid: 2821, threadinfo ffff8810589a8000, task ffff8810592f8000) >> [ 126.644007] Stack: >> [ 126.644007] ffff8810589a9e08 ffffffff810be37f ffffffffa0146220 ffffffff81a7b390 >> [ 126.644007] ffff8810589a9e58 ffffffff81317eb3 ffff8810589a9e48 0000000000000000 >> [ 126.644007] ffffffff81a342c0 ffffffff81a342e0 0000000000000000 ffffffffa0146220 >> [ 126.644007] Call Trace: >> [ 126.644007] [<ffffffff810be37f>] ? tracepoint_module_notify+0xd9/0x14a >> [ 126.644007] [<ffffffff81317eb3>] class_interface_register+0x4a/0xbc >> [ 126.644007] [<ffffffffa00b8000>] ? 0xffffffffa00b7fff >> [ 126.644007] [<ffffffffa00b8010>] acpihp_drv_init+0x10/0x12 [acpihp_drv] >> [ 126.644007] [<ffffffff8100207f>] do_one_initcall+0x7f/0x139 >> [ 126.644007] [<ffffffff81093414>] sys_init_module+0x12d3/0x14e3 >> [ 126.644007] [<ffffffff81264b0d>] ? ddebug_dyndbg_boot_param_cb+0x45/0x45 >> [ 126.644007] [<ffffffff814c9469>] system_call_fastpath+0x16/0x1b >> [ 126.644007] Code: 48 8b 04 25 80 c6 00 00 48 89 43 18 31 c0 5b 5b c9 c3 55 48 89 e5 53 48 83 ec 18 66 66 66 66 90 48 89 fb e8 5a 0c 00 00 48 89 df <f0> ff 0f 79 05 e8 06 ff ff ff 65 48 8b 04 25 80 c6 00 00 48 89 >> [ 126.644007] RIP [<ffffffff814c0cd3>] mutex_lock+0x19/0x37 >> [ 126.644007] RSP <ffff8810589a9de8> >> [ 126.644007] CR2: 0000000000000078 >> [ 129.981335] ---[ end trace da17e9c9de8dd560 ]--- >> [ 139.085895] nr_pdflush_threads exported in /proc is scheduled for removal >> [ 139.167394] sysctl: The scan_unevictable_pages sysctl/node-interface has been disabled for lack of a legitimate use case. If you have one, please send an email to linux-mm@kvack.org. >> >> Looks like it dereferenced a NULL pointer here. >> May be it was my mistake that I didn't configure the environment correctly. >> Would you please give me some advice ? >> >> Thanks. :) >> >>
On 2012-8-9 16:41, Tang Chen wrote: > Hi Liu ~ > > On 08/09/2012 03:40 PM, Jiang Liu wrote: >> Hi Tang, >> Thanks for testing. >> Currently there's a limitation that you need to insert acpihp_enum driver first. > > Sorry, I didn't make it clear. I did load acpihp_enum module first, and then load acpihp_drv. > > And I just tried it some more times. It just hung up, but dmesg had no output. > Like this: > > # modprobe acpihp_enum > (OK, and sysfs interfaces have been created) > # modprobe acpihp_drv > (hang up) > > # dmesg > (nothing) > > The "modprobe acpihp_drv" process's call trace shows that it hung at the following function: > #0 0x00000032836aab80 in __nanosleep_nocancel () from /lib64/libc.so.6 > #1 0x00000032836deb64 in usleep () from /lib64/libc.so.6 > ...... > > I have tried several times and I cannot reproduce the situation I just said. You can reproduce it by loading acpihp_drv without acpihp_enum driver, I guess. The acpihp_drv module_init() should call acpihp_register_class() to initialize the core. > Maybe my box has something different with yours. And I'll try to find out why. > > Thanks for your advice. :) > >> Will fix this issue in next version. >> Regards! >> Gerry >> >> On 2012-8-9 15:12, Tang Chen wrote: >>> Hi Liu~ >>> >>> I compiled this driver as a module, acpihp_drv. And when I loaded this module, it >>> gave the following error message: >>> >>> # modprobe acpihp_drv >>> (the command hangs up, no return after 10 min) >>> >>> #dmesg >>> ...... >>> [ 126.643350] BUG: unable to handle kernel NULL pointer dereference at 0000000000000078 >>> [ 126.644007] IP: [<ffffffff814c0cd3>] mutex_lock+0x19/0x37 >>> [ 126.644007] PGD 105277a067 PUD 104f823067 PMD 0 >>> [ 126.644007] Oops: 0002 [#1] SMP >>> [ 126.644007] Modules linked in: acpihp_drv(+) ebtable_nat ebtables ipt_MASQUERADE iptable_nat nf_nat iptable_mangle bridge stp llc sunrpc cpufreq_ondemand acpi_cpufreq freq_table mperf ip6t_REJECT nf_conntrack_ipv6 nf_defrag_ipv6 ip6table_filter ip6_tables ipv6 vhost_net macvtap macvlan tun uinput iTCO_wdt iTCO_vendor_support coretemp kvm_intel kvm crc32c_intel microcode lpc_ich mfd_core pcspkr i2c_i801 i2c_core ioatdma e1000e acpi_memhotplug i7core_edac edac_core igb dca mptsas mptscsih mptbase scsi_transport_sas >>> [ 126.644007] CPU 10 >>> [ 126.644007] Pid: 2821, comm: modprobe Tainted: G A 3.6.0-rc1+ #6 FUJITSU-SV PRIMEQUEST 1800E/SB >>> [ 126.644007] RIP: 0010:[<ffffffff814c0cd3>] [<ffffffff814c0cd3>] mutex_lock+0x19/0x37 >>> [ 126.644007] RSP: 0018:ffff8810589a9de8 EFLAGS: 00010246 >>> [ 126.644007] RAX: 0000000000000000 RBX: 0000000000000078 RCX: 0000000000000000 >>> [ 126.644007] RDX: 0000000000000000 RSI: 0000000000000000 RDI: 0000000000000078 >>> [ 126.644007] RBP: ffff8810589a9e08 R08: 0000000000000000 R09: ffff8810589a9d88 >>> [ 126.644007] R10: 00000000000013e5 R11: 00000000000013e5 R12: ffffffffa01460d0 >>> [ 126.644007] R13: 0000000000000000 R14: ffffffffa014732b R15: 00000000000000bf >>> [ 126.644007] FS: 00007fecb1802700(0000) GS:ffff88105e640000(0000) knlGS:0000000000000000 >>> [ 126.644007] CS: 0010 DS: 0000 ES: 0000 CR0: 0000000080050033 >>> [ 126.644007] CR2: 0000000000000078 CR3: 0000001052772000 CR4: 00000000000007e0 >>> [ 126.644007] DR0: 0000000000000000 DR1: 0000000000000000 DR2: 0000000000000000 >>> [ 126.644007] DR3: 0000000000000000 DR6: 00000000ffff0ff0 DR7: 0000000000000400 >>> [ 126.644007] Process modprobe (pid: 2821, threadinfo ffff8810589a8000, task ffff8810592f8000) >>> [ 126.644007] Stack: >>> [ 126.644007] ffff8810589a9e08 ffffffff810be37f ffffffffa0146220 ffffffff81a7b390 >>> [ 126.644007] ffff8810589a9e58 ffffffff81317eb3 ffff8810589a9e48 0000000000000000 >>> [ 126.644007] ffffffff81a342c0 ffffffff81a342e0 0000000000000000 ffffffffa0146220 >>> [ 126.644007] Call Trace: >>> [ 126.644007] [<ffffffff810be37f>] ? tracepoint_module_notify+0xd9/0x14a >>> [ 126.644007] [<ffffffff81317eb3>] class_interface_register+0x4a/0xbc >>> [ 126.644007] [<ffffffffa00b8000>] ? 0xffffffffa00b7fff >>> [ 126.644007] [<ffffffffa00b8010>] acpihp_drv_init+0x10/0x12 [acpihp_drv] >>> [ 126.644007] [<ffffffff8100207f>] do_one_initcall+0x7f/0x139 >>> [ 126.644007] [<ffffffff81093414>] sys_init_module+0x12d3/0x14e3 >>> [ 126.644007] [<ffffffff81264b0d>] ? ddebug_dyndbg_boot_param_cb+0x45/0x45 >>> [ 126.644007] [<ffffffff814c9469>] system_call_fastpath+0x16/0x1b >>> [ 126.644007] Code: 48 8b 04 25 80 c6 00 00 48 89 43 18 31 c0 5b 5b c9 c3 55 48 89 e5 53 48 83 ec 18 66 66 66 66 90 48 89 fb e8 5a 0c 00 00 48 89 df <f0> ff 0f 79 05 e8 06 ff ff ff 65 48 8b 04 25 80 c6 00 00 48 89 >>> [ 126.644007] RIP [<ffffffff814c0cd3>] mutex_lock+0x19/0x37 >>> [ 126.644007] RSP <ffff8810589a9de8> >>> [ 126.644007] CR2: 0000000000000078 >>> [ 129.981335] ---[ end trace da17e9c9de8dd560 ]--- >>> [ 139.085895] nr_pdflush_threads exported in /proc is scheduled for removal >>> [ 139.167394] sysctl: The scan_unevictable_pages sysctl/node-interface has been disabled for lack of a legitimate use case. If you have one, please send an email to linux-mm@kvack.org. >>> >>> Looks like it dereferenced a NULL pointer here. >>> May be it was my mistake that I didn't configure the environment correctly. >>> Would you please give me some advice ? >>> >>> Thanks. :) >>> >>> > -- To unsubscribe from this list: send the line "unsubscribe linux-pci" in the body of a message to majordomo@vger.kernel.org More majordomo info at http://vger.kernel.org/majordomo-info.html
On 08/09/2012 05:36 PM, Jiang Liu wrote: >> And I just tried it some more times. It just hung up, but dmesg had no output. >> Like this: >> >> # modprobe acpihp_enum >> (OK, and sysfs interfaces have been created) >> # modprobe acpihp_drv >> (hang up) >> >> # dmesg >> (nothing) >> >> The "modprobe acpihp_drv" process's call trace shows that it hung at the following function: >> #0 0x00000032836aab80 in __nanosleep_nocancel () from /lib64/libc.so.6 >> #1 0x00000032836deb64 in usleep () from /lib64/libc.so.6 >> ...... >> >> I have tried several times and I cannot reproduce the situation I just said. > You can reproduce it by loading acpihp_drv without acpihp_enum driver, I guess. > The acpihp_drv module_init() should call acpihp_register_class() to initialize the core. > Hi~ True. Thanks for your comments. :) Since I'm new in PCI related area, if you don't mind, would you please give me some more advice about the following problem ? Thanks. :) "modprobe acpihp_drv" command failed, but acpihp_drv was loaded successfully, and always in use. It cannot be removed. Is it a problem ? [root@DP tangchen]# lsmod | grep acpi acpi_cpufreq 9542 0 freq_table 5030 2 cpufreq_ondemand,acpi_cpufreq mperf 1391 1 acpi_cpufreq acpi_memhotplug 4414 0 [root@DP tangchen]# modprobe acpihp_drv Killed (NOTE: The NULL pointer problem happened here.) [root@DP tangchen]# echo $? 137 [root@DP tangchen]# lsmod | grep acpi acpihp_drv 24925 1 (NOTE: Here, the module is loaded.) acpi_cpufreq 9542 0 freq_table 5030 2 cpufreq_ondemand,acpi_cpufreq mperf 1391 1 acpi_cpufreq acpi_memhotplug 4414 0 [root@DP tangchen]# rmmod acpihp_drv ERROR: Module acpihp_drv is in use The core.c file has been compiled into kernel because of my configuration "CONFIG_ACPI_HOTPLUG=y". As my colleague said, in this case, there is no dependency between acpihp_enum and acpihp_drv. So I think, do we need to compile core.c into acpihp_enum module, or simply check if acpihp_enum has been loaded in acpihp_drv_init() ? I am not sure if it is a good idea to move acpihp_slot_class definition and all related API to acpihp_enum module. Thanks again for your comments and patient. :)
diff --git a/drivers/acpi/Kconfig b/drivers/acpi/Kconfig index c9f7918..89047a3 100644 --- a/drivers/acpi/Kconfig +++ b/drivers/acpi/Kconfig @@ -354,6 +354,18 @@ config ACPI_HOTPLUG_ENUM_EJ0 It's the default method to detect ACPI hotplug slots. +config ACPI_HOTPLUG_DRIVER + tristate "ACPI Based System Device Hotplug Driver" + depends on ACPI_HOTPLUG + default y + help + This driver enables ACPI based system device hotplug, including + physical processor, memory device, IO host bridge and computer + node etc. + + 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 25fac24..d69832f 100644 --- a/drivers/acpi/hotplug/Makefile +++ b/drivers/acpi/hotplug/Makefile @@ -8,3 +8,6 @@ acpihp-y = core.o device.o obj-$(CONFIG_ACPI_HOTPLUG_ENUM) += acpihp_enum.o acpihp_enum-y = slot_enum.o acpihp_enum-y += slot_enum_ej0.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..18330f7 --- /dev/null +++ b/drivers/acpi/hotplug/acpihp_drv.h @@ -0,0 +1,62 @@ +/* + * Copyright (C) 2011 Huawei Tech. Co., Ltd. + * Copyright (C) 2011 Jiang Liu <jiang.liu@huawei.com> + * Copyright (C) 2011 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_status; + atomic_t cancel_flag; + 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..538772d --- /dev/null +++ b/drivers/acpi/hotplug/drv_main.c @@ -0,0 +1,331 @@ +/* + * Copyright (C) 2011 Huawei Tech. Co., Ltd. + * Copyright (C) 2011 Jiang Liu <jiang.liu@huawei.com> + * Copyright (C) 2011 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 state 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_MEM; type <= ACPIHP_DEV_TYPE_HOST_BRIDGE; + 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_DEBUG("fails to get info about %s.\n", + dev_name(&dev->dev)); + 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); + } + } + +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_OK; + 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_DEBUG("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_DEBUG("fails to add %s to slot %s.\n", + dev_name(&dev->dev), slot->name); + rv = AE_ERROR; + } + } else { + ACPIHP_DEBUG("fails to query device info of %s.\n", + dev_name(&dev->dev)); + } + + 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_DEBUG("%s to install event handler for %s.\n", + ACPI_SUCCESS(status) ? "succeeds" : "fails", slot->name); + + 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_DEBUG("%s to uninstall event handler for %s.\n", + ACPI_SUCCESS(status) ? "succeeds" : "fails", slot->name); +} + +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_DEBUG("fails to get reference to slot_ops for %s.\n", + slot->name); + return -EINVAL; + } + + /* install ACPI event notification handler for slot */ + if (ACPI_FAILURE(acpihp_drv_install_handler(slot))) { + ACPIHP_DEBUG("fails to install event handler for %s.\n", + slot->name); + module_put(slot->slot_ops->owner); + return -EBUSY; + } + + /* Enumerate all devices if slot is already powered. */ + if (!acpihp_slot_powered(slot)) + ACPIHP_DEBUG("slot %s is powered off.\n", slot->name); + 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_DEBUG("fails to attach driver data for %s.\n", + slot->name); + acpihp_drv_remove_devices(slot); + module_put(slot->slot_ops->owner); + kfree(drv_data); + return -ENOMEM; + } + + ACPIHP_INFO("found hotplug slot %s.\n", slot->full_path); + + 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_INFO("remove hotplug slot %s.\n", slot->full_path); + + 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) +{ + return class_interface_register(&acpihp_drv_interface); +} + +static void __exit acpihp_drv_exit(void) +{ + class_interface_unregister(&acpihp_drv_interface); +} + +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>");