aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVaidyanathan Srinivasan <svaidy@linux.vnet.ibm.com>2010-10-06 04:37:09 -0400
committerBenjamin Herrenschmidt <benh@kernel.crashing.org>2010-11-28 23:48:19 -0500
commit5742bd859524793b857110a13db349d95070d2dc (patch)
tree600b1b0bf9d6edc0bf210ea7a9cffe5f60517eb3
parent99d86705253dcf728dbbec4d694a6764b6edb70c (diff)
powerpc: Add support for new hcall H_BEST_ENERGY
Create sysfs interface to export data from H_BEST_ENERGY hcall that can be used by administrative tools on supported pseries platforms for energy management optimizations. sys/device/system/cpu/pseries_(de)activate_hint_list and sys/device/system/cpu/cpuN/pseries_(de)activate_hint will provide hints for activation and deactivation of cpus respectively. These hints are abstract number given by the hypervisor based on the extended knowledge the hypervisor has regarding the system topology and resource mappings. The activate and the deactivate sysfs entry is for the two distinct operations that we could do for energy savings. When we have more capacity than required, we could deactivate few core to save energy. The choice of the core to deactivate will be based on /sys/devices/system/cpu/deactivate_hint_list. The comma separated list of cpus (cores) will be the preferred choice. If we have to activate some of the deactivated cores, then /sys/devices/system/cpu/activate_hint_list will be used. The per-cpu file /sys/device/system/cpu/cpuN/pseries_(de)activate_hint further provide more fine grain information by exporting the value of the hint itself. Added new driver module arch/powerpc/platforms/pseries/pseries_energy.c under new config option CONFIG_PSERIES_ENERGY Signed-off-by: Vaidyanathan Srinivasan <svaidy@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
-rw-r--r--arch/powerpc/include/asm/hvcall.h3
-rw-r--r--arch/powerpc/platforms/pseries/Kconfig10
-rw-r--r--arch/powerpc/platforms/pseries/Makefile1
-rw-r--r--arch/powerpc/platforms/pseries/pseries_energy.c326
4 files changed, 339 insertions, 1 deletions
diff --git a/arch/powerpc/include/asm/hvcall.h b/arch/powerpc/include/asm/hvcall.h
index de03ca58db5d..bf86b03d5af2 100644
--- a/arch/powerpc/include/asm/hvcall.h
+++ b/arch/powerpc/include/asm/hvcall.h
@@ -232,7 +232,8 @@
232#define H_GET_EM_PARMS 0x2B8 232#define H_GET_EM_PARMS 0x2B8
233#define H_SET_MPP 0x2D0 233#define H_SET_MPP 0x2D0
234#define H_GET_MPP 0x2D4 234#define H_GET_MPP 0x2D4
235#define MAX_HCALL_OPCODE H_GET_MPP 235#define H_BEST_ENERGY 0x2F4
236#define MAX_HCALL_OPCODE H_BEST_ENERGY
236 237
237#ifndef __ASSEMBLY__ 238#ifndef __ASSEMBLY__
238 239
diff --git a/arch/powerpc/platforms/pseries/Kconfig b/arch/powerpc/platforms/pseries/Kconfig
index 3139814f6439..5d1b743dbe7e 100644
--- a/arch/powerpc/platforms/pseries/Kconfig
+++ b/arch/powerpc/platforms/pseries/Kconfig
@@ -33,6 +33,16 @@ config PSERIES_MSI
33 depends on PCI_MSI && EEH 33 depends on PCI_MSI && EEH
34 default y 34 default y
35 35
36config PSERIES_ENERGY
37 tristate "pSeries energy management capabilities driver"
38 depends on PPC_PSERIES
39 default y
40 help
41 Provides interface to platform energy management capabilities
42 on supported PSERIES platforms.
43 Provides: /sys/devices/system/cpu/pseries_(de)activation_hint_list
44 and /sys/devices/system/cpu/cpuN/pseries_(de)activation_hint
45
36config SCANLOG 46config SCANLOG
37 tristate "Scanlog dump interface" 47 tristate "Scanlog dump interface"
38 depends on RTAS_PROC && PPC_PSERIES 48 depends on RTAS_PROC && PPC_PSERIES
diff --git a/arch/powerpc/platforms/pseries/Makefile b/arch/powerpc/platforms/pseries/Makefile
index 59eb8bdaa79d..fc5237810ece 100644
--- a/arch/powerpc/platforms/pseries/Makefile
+++ b/arch/powerpc/platforms/pseries/Makefile
@@ -11,6 +11,7 @@ obj-$(CONFIG_EEH) += eeh.o eeh_cache.o eeh_driver.o eeh_event.o eeh_sysfs.o
11obj-$(CONFIG_KEXEC) += kexec.o 11obj-$(CONFIG_KEXEC) += kexec.o
12obj-$(CONFIG_PCI) += pci.o pci_dlpar.o 12obj-$(CONFIG_PCI) += pci.o pci_dlpar.o
13obj-$(CONFIG_PSERIES_MSI) += msi.o 13obj-$(CONFIG_PSERIES_MSI) += msi.o
14obj-$(CONFIG_PSERIES_ENERGY) += pseries_energy.o
14 15
15obj-$(CONFIG_HOTPLUG_CPU) += hotplug-cpu.o 16obj-$(CONFIG_HOTPLUG_CPU) += hotplug-cpu.o
16obj-$(CONFIG_MEMORY_HOTPLUG) += hotplug-memory.o 17obj-$(CONFIG_MEMORY_HOTPLUG) += hotplug-memory.o
diff --git a/arch/powerpc/platforms/pseries/pseries_energy.c b/arch/powerpc/platforms/pseries/pseries_energy.c
new file mode 100644
index 000000000000..c8b3c69fe891
--- /dev/null
+++ b/arch/powerpc/platforms/pseries/pseries_energy.c
@@ -0,0 +1,326 @@
1/*
2 * POWER platform energy management driver
3 * Copyright (C) 2010 IBM Corporation
4 *
5 * This program is free software; you can redistribute it and/or
6 * modify it under the terms of the GNU General Public License
7 * version 2 as published by the Free Software Foundation.
8 *
9 * This pseries platform device driver provides access to
10 * platform energy management capabilities.
11 */
12
13#include <linux/module.h>
14#include <linux/types.h>
15#include <linux/errno.h>
16#include <linux/init.h>
17#include <linux/seq_file.h>
18#include <linux/sysdev.h>
19#include <linux/cpu.h>
20#include <linux/of.h>
21#include <asm/cputhreads.h>
22#include <asm/page.h>
23#include <asm/hvcall.h>
24
25
26#define MODULE_VERS "1.0"
27#define MODULE_NAME "pseries_energy"
28
29/* Driver flags */
30
31static int sysfs_entries;
32
33/* Helper routines */
34
35/*
36 * Routine to detect firmware support for hcall
37 * return 1 if H_BEST_ENERGY is supported
38 * else return 0
39 */
40
41static int check_for_h_best_energy(void)
42{
43 struct device_node *rtas = NULL;
44 const char *hypertas, *s;
45 int length;
46 int rc = 0;
47
48 rtas = of_find_node_by_path("/rtas");
49 if (!rtas)
50 return 0;
51
52 hypertas = of_get_property(rtas, "ibm,hypertas-functions", &length);
53 if (!hypertas) {
54 of_node_put(rtas);
55 return 0;
56 }
57
58 /* hypertas will have list of strings with hcall names */
59 for (s = hypertas; s < hypertas + length; s += strlen(s) + 1) {
60 if (!strncmp("hcall-best-energy-1", s, 19)) {
61 rc = 1; /* Found the string */
62 break;
63 }
64 }
65 of_node_put(rtas);
66 return rc;
67}
68
69/* Helper Routines to convert between drc_index to cpu numbers */
70
71static u32 cpu_to_drc_index(int cpu)
72{
73 struct device_node *dn = NULL;
74 const int *indexes;
75 int i;
76 int rc = 1;
77 u32 ret = 0;
78
79 dn = of_find_node_by_path("/cpus");
80 if (dn == NULL)
81 goto err;
82 indexes = of_get_property(dn, "ibm,drc-indexes", NULL);
83 if (indexes == NULL)
84 goto err_of_node_put;
85 /* Convert logical cpu number to core number */
86 i = cpu_core_index_of_thread(cpu);
87 /*
88 * The first element indexes[0] is the number of drc_indexes
89 * returned in the list. Hence i+1 will get the drc_index
90 * corresponding to core number i.
91 */
92 WARN_ON(i > indexes[0]);
93 ret = indexes[i + 1];
94 rc = 0;
95
96err_of_node_put:
97 of_node_put(dn);
98err:
99 if (rc)
100 printk(KERN_WARNING "cpu_to_drc_index(%d) failed", cpu);
101 return ret;
102}
103
104static int drc_index_to_cpu(u32 drc_index)
105{
106 struct device_node *dn = NULL;
107 const int *indexes;
108 int i, cpu = 0;
109 int rc = 1;
110
111 dn = of_find_node_by_path("/cpus");
112 if (dn == NULL)
113 goto err;
114 indexes = of_get_property(dn, "ibm,drc-indexes", NULL);
115 if (indexes == NULL)
116 goto err_of_node_put;
117 /*
118 * First element in the array is the number of drc_indexes
119 * returned. Search through the list to find the matching
120 * drc_index and get the core number
121 */
122 for (i = 0; i < indexes[0]; i++) {
123 if (indexes[i + 1] == drc_index)
124 break;
125 }
126 /* Convert core number to logical cpu number */
127 cpu = cpu_first_thread_of_core(i);
128 rc = 0;
129
130err_of_node_put:
131 of_node_put(dn);
132err:
133 if (rc)
134 printk(KERN_WARNING "drc_index_to_cpu(%d) failed", drc_index);
135 return cpu;
136}
137
138/*
139 * pseries hypervisor call H_BEST_ENERGY provides hints to OS on
140 * preferred logical cpus to activate or deactivate for optimized
141 * energy consumption.
142 */
143
144#define FLAGS_MODE1 0x004E200000080E01
145#define FLAGS_MODE2 0x004E200000080401
146#define FLAGS_ACTIVATE 0x100
147
148static ssize_t get_best_energy_list(char *page, int activate)
149{
150 int rc, cnt, i, cpu;
151 unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
152 unsigned long flags = 0;
153 u32 *buf_page;
154 char *s = page;
155
156 buf_page = (u32 *) get_zeroed_page(GFP_KERNEL);
157 if (!buf_page)
158 return -ENOMEM;
159
160 flags = FLAGS_MODE1;
161 if (activate)
162 flags |= FLAGS_ACTIVATE;
163
164 rc = plpar_hcall9(H_BEST_ENERGY, retbuf, flags, 0, __pa(buf_page),
165 0, 0, 0, 0, 0, 0);
166 if (rc != H_SUCCESS) {
167 free_page((unsigned long) buf_page);
168 return -EINVAL;
169 }
170
171 cnt = retbuf[0];
172 for (i = 0; i < cnt; i++) {
173 cpu = drc_index_to_cpu(buf_page[2*i+1]);
174 if ((cpu_online(cpu) && !activate) ||
175 (!cpu_online(cpu) && activate))
176 s += sprintf(s, "%d,", cpu);
177 }
178 if (s > page) { /* Something to show */
179 s--; /* Suppress last comma */
180 s += sprintf(s, "\n");
181 }
182
183 free_page((unsigned long) buf_page);
184 return s-page;
185}
186
187static ssize_t get_best_energy_data(struct sys_device *dev,
188 char *page, int activate)
189{
190 int rc;
191 unsigned long retbuf[PLPAR_HCALL9_BUFSIZE];
192 unsigned long flags = 0;
193
194 flags = FLAGS_MODE2;
195 if (activate)
196 flags |= FLAGS_ACTIVATE;
197
198 rc = plpar_hcall9(H_BEST_ENERGY, retbuf, flags,
199 cpu_to_drc_index(dev->id),
200 0, 0, 0, 0, 0, 0, 0);
201
202 if (rc != H_SUCCESS)
203 return -EINVAL;
204
205 return sprintf(page, "%lu\n", retbuf[1] >> 32);
206}
207
208/* Wrapper functions */
209
210static ssize_t cpu_activate_hint_list_show(struct sysdev_class *class,
211 struct sysdev_class_attribute *attr, char *page)
212{
213 return get_best_energy_list(page, 1);
214}
215
216static ssize_t cpu_deactivate_hint_list_show(struct sysdev_class *class,
217 struct sysdev_class_attribute *attr, char *page)
218{
219 return get_best_energy_list(page, 0);
220}
221
222static ssize_t percpu_activate_hint_show(struct sys_device *dev,
223 struct sysdev_attribute *attr, char *page)
224{
225 return get_best_energy_data(dev, page, 1);
226}
227
228static ssize_t percpu_deactivate_hint_show(struct sys_device *dev,
229 struct sysdev_attribute *attr, char *page)
230{
231 return get_best_energy_data(dev, page, 0);
232}
233
234/*
235 * Create sysfs interface:
236 * /sys/devices/system/cpu/pseries_activate_hint_list
237 * /sys/devices/system/cpu/pseries_deactivate_hint_list
238 * Comma separated list of cpus to activate or deactivate
239 * /sys/devices/system/cpu/cpuN/pseries_activate_hint
240 * /sys/devices/system/cpu/cpuN/pseries_deactivate_hint
241 * Per-cpu value of the hint
242 */
243
244struct sysdev_class_attribute attr_cpu_activate_hint_list =
245 _SYSDEV_CLASS_ATTR(pseries_activate_hint_list, 0444,
246 cpu_activate_hint_list_show, NULL);
247
248struct sysdev_class_attribute attr_cpu_deactivate_hint_list =
249 _SYSDEV_CLASS_ATTR(pseries_deactivate_hint_list, 0444,
250 cpu_deactivate_hint_list_show, NULL);
251
252struct sysdev_attribute attr_percpu_activate_hint =
253 _SYSDEV_ATTR(pseries_activate_hint, 0444,
254 percpu_activate_hint_show, NULL);
255
256struct sysdev_attribute attr_percpu_deactivate_hint =
257 _SYSDEV_ATTR(pseries_deactivate_hint, 0444,
258 percpu_deactivate_hint_show, NULL);
259
260static int __init pseries_energy_init(void)
261{
262 int cpu, err;
263 struct sys_device *cpu_sys_dev;
264
265 if (!check_for_h_best_energy()) {
266 printk(KERN_INFO "Hypercall H_BEST_ENERGY not supported\n");
267 return 0;
268 }
269 /* Create the sysfs files */
270 err = sysfs_create_file(&cpu_sysdev_class.kset.kobj,
271 &attr_cpu_activate_hint_list.attr);
272 if (!err)
273 err = sysfs_create_file(&cpu_sysdev_class.kset.kobj,
274 &attr_cpu_deactivate_hint_list.attr);
275
276 if (err)
277 return err;
278 for_each_possible_cpu(cpu) {
279 cpu_sys_dev = get_cpu_sysdev(cpu);
280 err = sysfs_create_file(&cpu_sys_dev->kobj,
281 &attr_percpu_activate_hint.attr);
282 if (err)
283 break;
284 err = sysfs_create_file(&cpu_sys_dev->kobj,
285 &attr_percpu_deactivate_hint.attr);
286 if (err)
287 break;
288 }
289
290 if (err)
291 return err;
292
293 sysfs_entries = 1; /* Removed entries on cleanup */
294 return 0;
295
296}
297
298static void __exit pseries_energy_cleanup(void)
299{
300 int cpu;
301 struct sys_device *cpu_sys_dev;
302
303 if (!sysfs_entries)
304 return;
305
306 /* Remove the sysfs files */
307 sysfs_remove_file(&cpu_sysdev_class.kset.kobj,
308 &attr_cpu_activate_hint_list.attr);
309
310 sysfs_remove_file(&cpu_sysdev_class.kset.kobj,
311 &attr_cpu_deactivate_hint_list.attr);
312
313 for_each_possible_cpu(cpu) {
314 cpu_sys_dev = get_cpu_sysdev(cpu);
315 sysfs_remove_file(&cpu_sys_dev->kobj,
316 &attr_percpu_activate_hint.attr);
317 sysfs_remove_file(&cpu_sys_dev->kobj,
318 &attr_percpu_deactivate_hint.attr);
319 }
320}
321
322module_init(pseries_energy_init);
323module_exit(pseries_energy_cleanup);
324MODULE_DESCRIPTION("Driver for pSeries platform energy management");
325MODULE_AUTHOR("Vaidyanathan Srinivasan");
326MODULE_LICENSE("GPL");