From patchwork Sat Aug 4 12:13:59 2012 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Jiang Liu X-Patchwork-Id: 1273421 Return-Path: X-Original-To: patchwork-linux-acpi@patchwork.kernel.org Delivered-To: patchwork-process-083081@patchwork1.kernel.org Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by patchwork1.kernel.org (Postfix) with ESMTP id 311BB3FD2B for ; Sat, 4 Aug 2012 12:18:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1752211Ab2HDMSb (ORCPT ); Sat, 4 Aug 2012 08:18:31 -0400 Received: from szxga01-in.huawei.com ([119.145.14.64]:51019 "EHLO szxga01-in.huawei.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1752188Ab2HDMSa (ORCPT ); Sat, 4 Aug 2012 08:18:30 -0400 Received: from 172.24.2.119 (EHLO szxeml210-edg.china.huawei.com) ([172.24.2.119]) by szxrg01-dlp.huawei.com (MOS 4.3.4-GA FastPath queued) with ESMTP id AMQ61670; Sat, 04 Aug 2012 20:18:11 +0800 (CST) Received: from SZXEML408-HUB.china.huawei.com (10.82.67.95) by szxeml210-edg.china.huawei.com (172.24.2.183) with Microsoft SMTP Server (TLS) id 14.1.323.3; Sat, 4 Aug 2012 20:14:37 +0800 Received: from localhost (10.108.108.229) by szxeml408-hub.china.huawei.com (10.82.67.95) with Microsoft SMTP Server id 14.1.323.3; Sat, 4 Aug 2012 20:14:30 +0800 From: Jiang Liu To: Yinghai Lu , Yasuaki Ishimatsu , Kenji Kaneshige , Wen Congyang , Tang Chen , Taku Izumi CC: Jiang Liu , Tony Luck , Huang Ying , Bob Moore , Len Brown , "Srivatsa S. Bhat" , Bjorn Helgaas , , , , Jiang Liu , Hanjun Guo Subject: [RFC PATCH v2 12/16] ACPIHP: cancel inprogress ACPI system device hotplug operations Date: Sat, 4 Aug 2012 20:13:59 +0800 Message-ID: <1344082443-4608-13-git-send-email-jiang.liu@huawei.com> X-Mailer: git-send-email 1.7.11.msysgit.1 In-Reply-To: <1344082443-4608-1-git-send-email-jiang.liu@huawei.com> References: <1344082443-4608-1-git-send-email-jiang.liu@huawei.com> MIME-Version: 1.0 X-Originating-IP: [10.108.108.229] X-CFilter-Loop: Reflected Sender: linux-acpi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-acpi@vger.kernel.org From: Jiang Liu This patch implements functions to cancel inprogress ACPI system device hotplug operations. Signed-off-by: Jiang Liu Signed-off-by: Hanjun Guo --- drivers/acpi/hotplug/Makefile | 1 + drivers/acpi/hotplug/acpihp_drv.h | 18 +++- drivers/acpi/hotplug/cancel.c | 171 +++++++++++++++++++++++++++++++++++++ 3 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 drivers/acpi/hotplug/cancel.c diff --git a/drivers/acpi/hotplug/Makefile b/drivers/acpi/hotplug/Makefile index 8477c71..7e10f69 100644 --- a/drivers/acpi/hotplug/Makefile +++ b/drivers/acpi/hotplug/Makefile @@ -12,3 +12,4 @@ acpihp_enum-y += slot_enum_ej0.o obj-$(CONFIG_ACPI_HOTPLUG_DRIVER) += acpihp_drv.o acpihp_drv-y = drv_main.o acpihp_drv-y += dependency.o +acpihp_drv-y += cancel.o diff --git a/drivers/acpi/hotplug/acpihp_drv.h b/drivers/acpi/hotplug/acpihp_drv.h index c4ff91c..5d69272 100644 --- a/drivers/acpi/hotplug/acpihp_drv.h +++ b/drivers/acpi/hotplug/acpihp_drv.h @@ -44,12 +44,21 @@ enum acpihp_dev_event { ACPIHP_DRV_EVENT_FROM_OS }; +enum acpihp_drv_cancel_state { + ACPIHP_DRV_CANCEL_INIT = 0, + ACPIHP_DRV_CANCEL_STARTED, + ACPIHP_DRV_CANCEL_OK, + ACPIHP_DRV_CANCEL_FAILED, + ACPIHP_DRV_CANCEL_MISSED, + ACPIHP_DRV_CANCEL_FINISHED +}; + struct acpihp_slot_drv { enum acpihp_dev_event event_flag; struct mutex op_mutex; /* Prevent concurrent hotplugs. */ struct list_head depend_list; /* Dependency relationship */ atomic_t cancel_status; - atomic_t cancel_flag; + atomic_t cancel_users; struct acpihp_cancel_context cancel_ctx; }; @@ -73,4 +82,11 @@ int acpihp_drv_filter_dependency_list(struct list_head *old_head, int acpihp_drv_generate_dependency_list(struct acpihp_slot *slot, struct list_head *slot_list, enum acpihp_drv_cmd cmd); +void acpihp_drv_cancel_init(struct list_head *list); +void acpihp_drv_cancel_notify(struct acpihp_slot *slot, + enum acpihp_drv_cancel_state state); +void acpihp_drv_cancel_fini(struct list_head *list); +int acpihp_drv_cancel_start(struct list_head *list); +int acpihp_drv_cancel_wait(struct list_head *list); + #endif /* __ACPIHP_DRV_H__ */ diff --git a/drivers/acpi/hotplug/cancel.c b/drivers/acpi/hotplug/cancel.c new file mode 100644 index 0000000..6c4d95c --- /dev/null +++ b/drivers/acpi/hotplug/cancel.c @@ -0,0 +1,171 @@ +/* + * Copyright (C) 2011 Huawei Tech. Co., Ltd. + * Copyright (C) 2011 Jiang Liu + * Copyright (C) 2011 Hanjun Guo + * + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * + * 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 +#include +#include "acpihp_drv.h" + +/* + * Implements interfaces to cancel inprogress hotplug operations. + * Currently only CONFIGURE and RELEASE operation stages support cancellation. + * Caller must serialize calls to functions by holding state_machine_mutex: + * acpihp_drv_cancel_init() + * acpihp_drv_cancel_fini() + * acpihp_drv_cancel_start() + */ +static DECLARE_WAIT_QUEUE_HEAD(acpihp_drv_cancel_queue); + +static int acpihp_drv_check_cancel(struct acpihp_cancel_context *ctx) +{ + struct acpihp_slot_drv *drv_data; + + BUG_ON(ctx == NULL); + drv_data = container_of(ctx, struct acpihp_slot_drv, cancel_ctx); + + return atomic_read(&drv_data->cancel_status) != ACPIHP_DRV_CANCEL_INIT; +} + +void acpihp_drv_cancel_init(struct list_head *list) +{ + struct acpihp_slot_drv *drv_data; + struct acpihp_slot_dependency *dep; + + /* Wait for all cancellation threads to exit */ + list_for_each_entry(dep, list, node) { + acpihp_drv_get_data(dep->slot, &drv_data); + drv_data->cancel_ctx.check_cancel = acpihp_drv_check_cancel; + atomic_set(&drv_data->cancel_status, ACPIHP_DRV_CANCEL_INIT); + atomic_set(&drv_data->cancel_users, 0); + } +} + +void acpihp_drv_cancel_notify(struct acpihp_slot *slot, + enum acpihp_drv_cancel_state state) +{ + struct acpihp_slot_drv *drv_data; + + acpihp_drv_get_data(slot, &drv_data); + if (atomic_cmpxchg(&drv_data->cancel_status, ACPIHP_DRV_CANCEL_FINISHED, + ACPIHP_DRV_CANCEL_INIT) != ACPIHP_DRV_CANCEL_INIT) { + atomic_set(&drv_data->cancel_status, state); + wake_up_all(&acpihp_drv_cancel_queue); + } +} + +/* + * Wait for all cancellation threads to give up their reference count. + * + * Caller must provide mechanism to avoid currently running + * acpihp_drv_cancel_start() and acpihp_drv_cancel_fini() + * on the same list. + */ +void acpihp_drv_cancel_fini(struct list_head *list) +{ + int state; + struct acpihp_slot_drv *drv_data; + struct acpihp_slot_dependency *dep; + + list_for_each_entry(dep, list, node) { + acpihp_drv_get_data(dep->slot, &drv_data); + + /* + * Wake up all cancellation threads if they are still + * STARTED state. + */ + state = atomic_cmpxchg(&drv_data->cancel_status, + ACPIHP_DRV_CANCEL_MISSED, + ACPIHP_DRV_CANCEL_STARTED); + if (state == ACPIHP_DRV_CANCEL_STARTED) + wake_up_all(&acpihp_drv_cancel_queue); + + /* Wait for all cancellation threads to exit */ + wait_event(acpihp_drv_cancel_queue, + !atomic_read(&drv_data->cancel_users)); + } +} + +/* + * Start cancellation on a list of hotplug slots. + * + * Caller must provide mechanism to avoid currently running + * acpihp_drv_cancel_start() and acpihp_drv_cancel_fini() + * on the same list. + */ +int acpihp_drv_cancel_start(struct list_head *list) +{ + struct acpihp_slot_drv *drv_data; + struct acpihp_slot_dependency *dep; + + if (list_empty(list)) { + ACPIHP_DEBUG("dependency list is empty.\n"); + return -ENODEV; + } + + /* Start cancellation on all slots. */ + list_for_each_entry(dep, list, node) { + acpihp_drv_get_data(dep->slot, &drv_data); + atomic_inc(&drv_data->cancel_users); + atomic_cmpxchg(&drv_data->cancel_status, + ACPIHP_DRV_CANCEL_STARTED, + ACPIHP_DRV_CANCEL_INIT); + } + + return 0; +} + +/* + * Wait for all slots on the list to reach a stable state and then check + * cancellation result. + */ +int acpihp_drv_cancel_wait(struct list_head *list) +{ + int status, result = 0; + struct acpihp_slot_drv *drv_data; + struct acpihp_slot_dependency *dep; + + list_for_each_entry(dep, list, node) { + acpihp_drv_get_data(dep->slot, &drv_data); + wait_event(acpihp_drv_cancel_queue, + atomic_read(&drv_data->cancel_status) + != ACPIHP_DRV_CANCEL_STARTED); + + status = atomic_read(&drv_data->cancel_status); + if (status == ACPIHP_DRV_CANCEL_FAILED) { + ACPIHP_DEBUG("fails to cancel operatin for %s.\n", + dep->slot->name); + result = result ? : -EBUSY; + } else if (status == ACPIHP_DRV_CANCEL_MISSED) { + ACPIHP_DEBUG("misses to cancel operatin for %s.\n", + dep->slot->name); + result = result ? : -EBUSY; + } + + atomic_set(&drv_data->cancel_status, + ACPIHP_DRV_CANCEL_FINISHED); + atomic_dec(&drv_data->cancel_users); + wake_up_all(&acpihp_drv_cancel_queue); + } + + return result; +}