From patchwork Fri Aug 28 10:00:16 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Gautham R Shenoy X-Patchwork-Id: 32352 Return-Path: X-Original-To: patchwork-incoming@bilbo.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from ozlabs.org (ozlabs.org [203.10.76.45]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "mx.ozlabs.org", Issuer "CA Cert Signing Authority" (verified OK)) by bilbo.ozlabs.org (Postfix) with ESMTPS id F3DA9B85AC for ; Fri, 28 Aug 2009 20:01:10 +1000 (EST) Received: by ozlabs.org (Postfix) id E8F0DDDD0B; Fri, 28 Aug 2009 20:01:10 +1000 (EST) Delivered-To: patchwork-incoming@ozlabs.org Received: from bilbo.ozlabs.org (bilbo.ozlabs.org [203.10.76.25]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "bilbo.ozlabs.org", Issuer "CAcert Class 3 Root" (verified OK)) by ozlabs.org (Postfix) with ESMTPS id DA7F4DDD04 for ; Fri, 28 Aug 2009 20:01:10 +1000 (EST) Received: from bilbo.ozlabs.org (localhost [127.0.0.1]) by bilbo.ozlabs.org (Postfix) with ESMTP id 07F2CB7ED6 for ; Fri, 28 Aug 2009 20:00:45 +1000 (EST) Received: from e23smtp05.au.ibm.com (e23smtp05.au.ibm.com [202.81.31.147]) (using TLSv1 with cipher DHE-RSA-AES256-SHA (256/256 bits)) (Client CN "e23smtp05.au.ibm.com", Issuer "Equifax" (verified OK)) by bilbo.ozlabs.org (Postfix) with ESMTPS id 484B6B7C0E for ; Fri, 28 Aug 2009 20:00:36 +1000 (EST) Received: from d23relay04.au.ibm.com (d23relay04.au.ibm.com [202.81.31.246]) by e23smtp05.au.ibm.com (8.14.3/8.13.1) with ESMTP id n7S9vxWA029135 for ; Fri, 28 Aug 2009 19:57:59 +1000 Received: from d23av01.au.ibm.com (d23av01.au.ibm.com [9.190.234.96]) by d23relay04.au.ibm.com (8.13.8/8.13.8/NCO v10.0) with ESMTP id n7S9wxvs1638586 for ; Fri, 28 Aug 2009 19:58:59 +1000 Received: from d23av01.au.ibm.com (loopback [127.0.0.1]) by d23av01.au.ibm.com (8.14.3/8.13.1/NCO v10.0 AVout) with ESMTP id n7SA0TAX023264 for ; Fri, 28 Aug 2009 20:00:30 +1000 Received: from sofia.in.ibm.com ([9.78.193.153]) by d23av01.au.ibm.com (8.14.3/8.13.1/NCO v10.0 AVin) with ESMTP id n7SA0PI3023084; Fri, 28 Aug 2009 20:00:27 +1000 Received: from sofia.in.ibm.com (sofia.in.ibm.com [127.0.0.1]) by sofia.in.ibm.com (Postfix) with ESMTP id AAC5AB8FC0; Fri, 28 Aug 2009 15:30:16 +0530 (IST) Subject: [PATCH v2 1/2] cpu: Offline state Framework. To: Joel Schopp , Benjamin Herrenschmidt , Vaidyanathan Srinivasan , Peter Zijlstra , Dipankar Sarma From: Gautham R Shenoy Date: Fri, 28 Aug 2009 15:30:16 +0530 Message-ID: <20090828100016.10641.62621.stgit@sofia.in.ibm.com> In-Reply-To: <20090828095741.10641.32053.stgit@sofia.in.ibm.com> References: <20090828095741.10641.32053.stgit@sofia.in.ibm.com> User-Agent: StGit/0.14.3.384.g9ab0 MIME-Version: 1.0 Cc: "Darrick J. Wong" , linuxppc-dev@lists.ozlabs.org, linux-kernel@vger.kernel.org, Venkatesh Pallipadi X-BeenThere: linuxppc-dev@lists.ozlabs.org X-Mailman-Version: 2.1.12 Precedence: list List-Id: Linux on PowerPC Developers Mail List List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org Errors-To: linuxppc-dev-bounces+patchwork-incoming=ozlabs.org@lists.ozlabs.org Provide an interface by which the system administrator can decide what state should the CPU go to when it is offlined. To query the hotplug states, on needs to perform a read on the sysfs tunable: /sys/devices/system/cpu/cpu/available_hotplug_states To query or set the current state for a particular CPU, one needs to use the sysfs interface: /sys/devices/system/cpu/cpu/current_state This patch implements the architecture independent bits of the cpu-offline-state framework. Architectures which want to expose the multiple offline-states to the userspace are expected to write a driver which can register with this framework. Such a driver should: - Implement the callbacks defined in the structure struct cpu_offline_driver which can be called into by this framework when the corresponding sysfs interfaces are read or written into. - Ensure that the following operation puts the CPU in the same state as it did in the absence of the driver. echo 0 > /sys/devices/system/cpu/cpu/online This framework also serializes the writes to the "current_state" with respect to with the writes to the "online" sysfs tunable. Signed-off-by: Gautham R Shenoy --- drivers/base/cpu.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++++--- include/linux/cpu.h | 30 +++++++++ 2 files changed, 197 insertions(+), 9 deletions(-) diff --git a/drivers/base/cpu.c b/drivers/base/cpu.c index e62a4cc..73efc55 100644 --- a/drivers/base/cpu.c +++ b/drivers/base/cpu.c @@ -20,7 +20,161 @@ EXPORT_SYMBOL(cpu_sysdev_class); static DEFINE_PER_CPU(struct sys_device *, cpu_sys_devices); +struct sys_device *get_cpu_sysdev(unsigned cpu) +{ + if (cpu < nr_cpu_ids && cpu_possible(cpu)) + return per_cpu(cpu_sys_devices, cpu); + else + return NULL; +} +EXPORT_SYMBOL_GPL(get_cpu_sysdev); + + #ifdef CONFIG_HOTPLUG_CPU + +struct cpu_offline_driver *cpu_offline_driver; +static DEFINE_MUTEX(cpu_offline_driver_lock); + +ssize_t show_available_states(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) +{ + struct cpu *cpu = container_of(dev, struct cpu, sysdev); + int cpu_num = cpu->sysdev.id; + ssize_t ret; + + mutex_lock(&cpu_offline_driver_lock); + if (!cpu_offline_driver) { + ret = -EEXIST; + goto out_unlock; + } + + ret = cpu_offline_driver->read_available_states(cpu_num, buf); + +out_unlock: + mutex_unlock(&cpu_offline_driver_lock); + + return ret; + +} + +ssize_t show_current_state(struct sys_device *dev, + struct sysdev_attribute *attr, char *buf) +{ + struct cpu *cpu = container_of(dev, struct cpu, sysdev); + int cpu_num = cpu->sysdev.id; + ssize_t ret = 0; + + mutex_lock(&cpu_offline_driver_lock); + if (!cpu_offline_driver) { + ret = -EEXIST; + goto out_unlock; + } + + ret = cpu_offline_driver->read_current_state(cpu_num, buf); + +out_unlock: + mutex_unlock(&cpu_offline_driver_lock); + + return ret; +} + +ssize_t store_current_state(struct sys_device *dev, + struct sysdev_attribute *attr, + const char *buf, size_t count) +{ + struct cpu *cpu = container_of(dev, struct cpu, sysdev); + int cpu_num = cpu->sysdev.id; + ssize_t ret = count; + + mutex_lock(&cpu_offline_driver_lock); + if (!cpu_offline_driver) { + ret = -EEXIST; + goto out_unlock; + } + + ret = cpu_offline_driver->write_current_state(cpu_num, buf); + +out_unlock: + mutex_unlock(&cpu_offline_driver_lock); + + if (ret >= 0) + ret = count; + return ret; +} + +static SYSDEV_ATTR(available_hotplug_states, 0444, show_available_states, + NULL); +static SYSDEV_ATTR(current_state, 0644, show_current_state, + store_current_state); + +/* Should be called with cpu_offline_driver_lock held */ +void cpu_offline_driver_add_cpu(struct sys_device *cpu_sys_dev) +{ + if (!cpu_offline_driver || !cpu_sys_dev) + return; + + sysdev_create_file(cpu_sys_dev, &attr_available_hotplug_states); + sysdev_create_file(cpu_sys_dev, &attr_current_state); +} + +/* Should be called with cpu_offline_driver_lock held */ +void cpu_offline_driver_remove_cpu(struct sys_device *cpu_sys_dev) +{ + if (!cpu_offline_driver || !cpu_sys_dev) + return; + + sysdev_remove_file(cpu_sys_dev, &attr_available_hotplug_states); + sysdev_remove_file(cpu_sys_dev, &attr_current_state); + +} + +int register_cpu_offline_driver(struct cpu_offline_driver *arch_cpu_driver) +{ + int ret = 0; + int cpu; + mutex_lock(&cpu_offline_driver_lock); + + if (cpu_offline_driver != NULL) { + ret = -EEXIST; + goto out_unlock; + } + + if (!(arch_cpu_driver->read_available_states && + arch_cpu_driver->read_current_state && + arch_cpu_driver->write_current_state)) { + ret = -EINVAL; + goto out_unlock; + } + + cpu_offline_driver = arch_cpu_driver; + + for_each_possible_cpu(cpu) + cpu_offline_driver_add_cpu(get_cpu_sysdev(cpu)); + +out_unlock: + mutex_unlock(&cpu_offline_driver_lock); + return ret; +} + +void unregister_cpu_offline_driver(struct cpu_offline_driver *arch_cpu_driver) +{ + int cpu; + mutex_lock(&cpu_offline_driver_lock); + + if (!cpu_offline_driver) { + WARN_ON(1); + mutex_unlock(&cpu_offline_driver_lock); + return; + } + + for_each_possible_cpu(cpu) + cpu_offline_driver_remove_cpu(get_cpu_sysdev(cpu)); + + cpu_offline_driver = NULL; + mutex_unlock(&cpu_offline_driver_lock); +} + + static ssize_t show_online(struct sys_device *dev, struct sysdev_attribute *attr, char *buf) { @@ -35,6 +189,7 @@ static ssize_t __ref store_online(struct sys_device *dev, struct sysdev_attribut struct cpu *cpu = container_of(dev, struct cpu, sysdev); ssize_t ret; + mutex_lock(&cpu_offline_driver_lock); switch (buf[0]) { case '0': ret = cpu_down(cpu->sysdev.id); @@ -50,6 +205,8 @@ static ssize_t __ref store_online(struct sys_device *dev, struct sysdev_attribut ret = -EINVAL; } + mutex_unlock(&cpu_offline_driver_lock); + if (ret >= 0) ret = count; return ret; @@ -59,23 +216,33 @@ static SYSDEV_ATTR(online, 0644, show_online, store_online); static void __cpuinit register_cpu_control(struct cpu *cpu) { sysdev_create_file(&cpu->sysdev, &attr_online); + mutex_lock(&cpu_offline_driver_lock); + cpu_offline_driver_add_cpu(&cpu->sysdev); + mutex_unlock(&cpu_offline_driver_lock); } + void unregister_cpu(struct cpu *cpu) { int logical_cpu = cpu->sysdev.id; unregister_cpu_under_node(logical_cpu, cpu_to_node(logical_cpu)); + mutex_lock(&cpu_offline_driver_lock); + cpu_offline_driver_remove_cpu(&cpu->sysdev); + mutex_unlock(&cpu_offline_driver_lock); + sysdev_remove_file(&cpu->sysdev, &attr_online); sysdev_unregister(&cpu->sysdev); per_cpu(cpu_sys_devices, logical_cpu) = NULL; return; } + #else /* ... !CONFIG_HOTPLUG_CPU */ static inline void register_cpu_control(struct cpu *cpu) { } + #endif /* CONFIG_HOTPLUG_CPU */ #ifdef CONFIG_KEXEC @@ -224,15 +391,6 @@ int __cpuinit register_cpu(struct cpu *cpu, int num) return error; } -struct sys_device *get_cpu_sysdev(unsigned cpu) -{ - if (cpu < nr_cpu_ids && cpu_possible(cpu)) - return per_cpu(cpu_sys_devices, cpu); - else - return NULL; -} -EXPORT_SYMBOL_GPL(get_cpu_sysdev); - int __init cpu_dev_init(void) { int err; diff --git a/include/linux/cpu.h b/include/linux/cpu.h index 4d668e0..7636420 100644 --- a/include/linux/cpu.h +++ b/include/linux/cpu.h @@ -51,6 +51,36 @@ struct notifier_block; #ifdef CONFIG_HOTPLUG_CPU extern int register_cpu_notifier(struct notifier_block *nb); extern void unregister_cpu_notifier(struct notifier_block *nb); + +/* + * struct cpu_offline_driver: Callbacks for cpu-offline state framework. + * + * Defines the hooks for the architecture dependent callbacks + * which can be invoked when the user queries the + * available_hotplug_states and current_state and sets the current_state. + * + * read_available_state: Called when the user queries available_hotplug_states. + * @cpu: Cpu for which available_hotplug_states are being queried. + * @buf: Buffer in which the available hotplug states are to be populated. + * + * read_current_state: Called when the user queries current_state. + * @cpu: Cpu for which current_state is being queried. + * @buf: Buffer in which the current_state value is to be returned. + * + * write_current_state: Called when the user wants to set the current_state. + * @cpu: Cpu for which the current state is being set. + * @buf: Buffer containing the string corresponding to the current_state + * that needs to be set. + */ +struct cpu_offline_driver { + ssize_t (*read_available_states)(unsigned int cpu, char *buf); + ssize_t (*read_current_state)(unsigned int cpu, char *buf); + ssize_t (*write_current_state)(unsigned int cpu, const char *buf); +}; + +extern int register_cpu_offline_driver(struct cpu_offline_driver *driver); +extern void unregister_cpu_offline_driver(struct cpu_offline_driver *driver); + #else #ifndef MODULE