Skip to content

Commit 32d8ad4

Browse files
bjking1ozbenh
authored andcommitted
powerpc/pseries: Partition hibernation support
Enables support for HMC initiated partition hibernation. This is a firmware assisted hibernation, since the firmware handles writing the memory out to disk, along with other partition information, so we just mimic suspend to ram. Signed-off-by: Brian King <brking@linux.vnet.ibm.com> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
1 parent 8fe93f8 commit 32d8ad4

File tree

5 files changed

+223
-1
lines changed

5 files changed

+223
-1
lines changed

arch/powerpc/Kconfig

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -219,7 +219,7 @@ config ARCH_HIBERNATION_POSSIBLE
219219
config ARCH_SUSPEND_POSSIBLE
220220
def_bool y
221221
depends on ADB_PMU || PPC_EFIKA || PPC_LITE5200 || PPC_83xx || \
222-
PPC_85xx || PPC_86xx
222+
PPC_85xx || PPC_86xx || PPC_PSERIES
223223

224224
config PPC_DCR_NATIVE
225225
bool

arch/powerpc/include/asm/machdep.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -266,6 +266,7 @@ struct machdep_calls {
266266
void (*suspend_disable_irqs)(void);
267267
void (*suspend_enable_irqs)(void);
268268
#endif
269+
int (*suspend_disable_cpu)(void);
269270

270271
#ifdef CONFIG_ARCH_CPU_PROBE_RELEASE
271272
ssize_t (*cpu_probe)(const char *, size_t);

arch/powerpc/platforms/pseries/Makefile

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,7 @@ obj-$(CONFIG_HCALL_STATS) += hvCall_inst.o
2626
obj-$(CONFIG_PHYP_DUMP) += phyp_dump.o
2727
obj-$(CONFIG_CMM) += cmm.o
2828
obj-$(CONFIG_DTL) += dtl.o
29+
30+
ifeq ($(CONFIG_PPC_PSERIES),y)
31+
obj-$(CONFIG_SUSPEND) += suspend.o
32+
endif

arch/powerpc/platforms/pseries/hotplug-cpu.c

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,9 @@ static void pseries_mach_cpu_die(void)
116116

117117
if (get_preferred_offline_state(cpu) == CPU_STATE_INACTIVE) {
118118
set_cpu_current_state(cpu, CPU_STATE_INACTIVE);
119+
if (ppc_md.suspend_disable_cpu)
120+
ppc_md.suspend_disable_cpu();
121+
119122
cede_latency_hint = 2;
120123

121124
get_lppaca()->idle = 1;
Lines changed: 214 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,214 @@
1+
/*
2+
* Copyright (C) 2010 Brian King IBM Corporation
3+
*
4+
* This program is free software; you can redistribute it and/or modify
5+
* it under the terms of the GNU General Public License as published by
6+
* the Free Software Foundation; either version 2 of the License, or
7+
* (at your option) any later version.
8+
*
9+
* This program is distributed in the hope that it will be useful,
10+
* but WITHOUT ANY WARRANTY; without even the implied warranty of
11+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12+
* GNU General Public License for more details.
13+
*
14+
* You should have received a copy of the GNU General Public License
15+
* along with this program; if not, write to the Free Software
16+
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
17+
*/
18+
19+
#include <linux/delay.h>
20+
#include <linux/suspend.h>
21+
#include <asm/firmware.h>
22+
#include <asm/hvcall.h>
23+
#include <asm/machdep.h>
24+
#include <asm/mmu.h>
25+
#include <asm/rtas.h>
26+
27+
static u64 stream_id;
28+
static struct sys_device suspend_sysdev;
29+
static DECLARE_COMPLETION(suspend_work);
30+
static struct rtas_suspend_me_data suspend_data;
31+
static atomic_t suspending;
32+
33+
/**
34+
* pseries_suspend_begin - First phase of hibernation
35+
*
36+
* Check to ensure we are in a valid state to hibernate
37+
*
38+
* Return value:
39+
* 0 on success / other on failure
40+
**/
41+
static int pseries_suspend_begin(suspend_state_t state)
42+
{
43+
long vasi_state, rc;
44+
unsigned long retbuf[PLPAR_HCALL_BUFSIZE];
45+
46+
/* Make sure the state is valid */
47+
rc = plpar_hcall(H_VASI_STATE, retbuf, stream_id);
48+
49+
vasi_state = retbuf[0];
50+
51+
if (rc) {
52+
pr_err("pseries_suspend_begin: vasi_state returned %ld\n",rc);
53+
return rc;
54+
} else if (vasi_state == H_VASI_ENABLED) {
55+
return -EAGAIN;
56+
} else if (vasi_state != H_VASI_SUSPENDING) {
57+
pr_err("pseries_suspend_begin: vasi_state returned state %ld\n",
58+
vasi_state);
59+
return -EIO;
60+
}
61+
62+
return 0;
63+
}
64+
65+
/**
66+
* pseries_suspend_cpu - Suspend a single CPU
67+
*
68+
* Makes the H_JOIN call to suspend the CPU
69+
*
70+
**/
71+
static int pseries_suspend_cpu(void)
72+
{
73+
if (atomic_read(&suspending))
74+
return rtas_suspend_cpu(&suspend_data);
75+
return 0;
76+
}
77+
78+
/**
79+
* pseries_suspend_enter - Final phase of hibernation
80+
*
81+
* Return value:
82+
* 0 on success / other on failure
83+
**/
84+
static int pseries_suspend_enter(suspend_state_t state)
85+
{
86+
int rc = rtas_suspend_last_cpu(&suspend_data);
87+
88+
atomic_set(&suspending, 0);
89+
atomic_set(&suspend_data.done, 1);
90+
return rc;
91+
}
92+
93+
/**
94+
* pseries_prepare_late - Prepare to suspend all other CPUs
95+
*
96+
* Return value:
97+
* 0 on success / other on failure
98+
**/
99+
static int pseries_prepare_late(void)
100+
{
101+
atomic_set(&suspending, 1);
102+
atomic_set(&suspend_data.working, 0);
103+
atomic_set(&suspend_data.done, 0);
104+
atomic_set(&suspend_data.error, 0);
105+
suspend_data.complete = &suspend_work;
106+
INIT_COMPLETION(suspend_work);
107+
return 0;
108+
}
109+
110+
/**
111+
* store_hibernate - Initiate partition hibernation
112+
* @classdev: sysdev class struct
113+
* @attr: class device attribute struct
114+
* @buf: buffer
115+
* @count: buffer size
116+
*
117+
* Write the stream ID received from the HMC to this file
118+
* to trigger hibernating the partition
119+
*
120+
* Return value:
121+
* number of bytes printed to buffer / other on failure
122+
**/
123+
static ssize_t store_hibernate(struct sysdev_class *classdev,
124+
struct sysdev_class_attribute *attr,
125+
const char *buf, size_t count)
126+
{
127+
int rc;
128+
129+
if (!capable(CAP_SYS_ADMIN))
130+
return -EPERM;
131+
132+
stream_id = simple_strtoul(buf, NULL, 16);
133+
134+
do {
135+
rc = pseries_suspend_begin(PM_SUSPEND_MEM);
136+
if (rc == -EAGAIN)
137+
ssleep(1);
138+
} while (rc == -EAGAIN);
139+
140+
if (!rc)
141+
rc = pm_suspend(PM_SUSPEND_MEM);
142+
143+
stream_id = 0;
144+
145+
if (!rc)
146+
rc = count;
147+
return rc;
148+
}
149+
150+
static SYSDEV_CLASS_ATTR(hibernate, S_IWUSR, NULL, store_hibernate);
151+
152+
static struct sysdev_class suspend_sysdev_class = {
153+
.name = "power",
154+
};
155+
156+
static struct platform_suspend_ops pseries_suspend_ops = {
157+
.valid = suspend_valid_only_mem,
158+
.begin = pseries_suspend_begin,
159+
.prepare_late = pseries_prepare_late,
160+
.enter = pseries_suspend_enter,
161+
};
162+
163+
/**
164+
* pseries_suspend_sysfs_register - Register with sysfs
165+
*
166+
* Return value:
167+
* 0 on success / other on failure
168+
**/
169+
static int pseries_suspend_sysfs_register(struct sys_device *sysdev)
170+
{
171+
int rc;
172+
173+
if ((rc = sysdev_class_register(&suspend_sysdev_class)))
174+
return rc;
175+
176+
sysdev->id = 0;
177+
sysdev->cls = &suspend_sysdev_class;
178+
179+
if ((rc = sysdev_class_create_file(&suspend_sysdev_class, &attr_hibernate)))
180+
goto class_unregister;
181+
182+
return 0;
183+
184+
class_unregister:
185+
sysdev_class_unregister(&suspend_sysdev_class);
186+
return rc;
187+
}
188+
189+
/**
190+
* pseries_suspend_init - initcall for pSeries suspend
191+
*
192+
* Return value:
193+
* 0 on success / other on failure
194+
**/
195+
static int __init pseries_suspend_init(void)
196+
{
197+
int rc;
198+
199+
if (!machine_is(pseries) || !firmware_has_feature(FW_FEATURE_LPAR))
200+
return 0;
201+
202+
suspend_data.token = rtas_token("ibm,suspend-me");
203+
if (suspend_data.token == RTAS_UNKNOWN_SERVICE)
204+
return 0;
205+
206+
if ((rc = pseries_suspend_sysfs_register(&suspend_sysdev)))
207+
return rc;
208+
209+
ppc_md.suspend_disable_cpu = pseries_suspend_cpu;
210+
suspend_set_ops(&pseries_suspend_ops);
211+
return 0;
212+
}
213+
214+
__initcall(pseries_suspend_init);

0 commit comments

Comments
 (0)