lkml.org 
[lkml]   [2012]   [Aug]   [4]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[RFC PATCH v2 12/16] ACPIHP: cancel inprogress ACPI system device hotplug operations
    Date
    From: Jiang Liu <jiang.liu@huawei.com>

    This patch implements functions to cancel inprogress ACPI system device
    hotplug operations.

    Signed-off-by: Jiang Liu <liuj97@gmail.com>
    Signed-off-by: Hanjun Guo <guohanjun@huawei.com>
    ---
    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 <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/wait.h>
    +#include <acpi/acpi_hotplug.h>
    +#include "acpihp_drv.h"
    +
    +/*
    + * Implements interfaces to cancel inprogress hotplug operations.
    + * Currently only CONFIGURE and RELEASE operation stages support cancellation.
    + * Caller must serialize calls to 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;
    +}
    --
    1.7.9.5



    \
     
     \ /
      Last update: 2012-08-04 15:01    [W:0.059 / U:35.168 seconds]
    ©2003-2016 Jasper Spaans. hosted at Digital OceanAdvertise on this site