Skip to content

Commit ab519a0

Browse files
Nathan Fontenotozbenh
authored andcommitted
powerpc/pseries: Kernel DLPAR Infrastructure
The Dynamic Logical Partitioning capabilities of the powerpc pseries platform allows for the addition and removal of resources (i.e. CPU's, memory, and PCI devices) from a partition. The removal of a resource involves removing the resource's node from the device tree and then returning the resource to firmware via the rtas set-indicator call. To add a resource, it is first obtained from firmware via the rtas set-indicator call and then a new device tree node is created using the ibm,configure-coinnector rtas call and added to the device tree. This patch provides the kernel DLPAR infrastructure in a new filed named dlpar.c. The functionality provided is for acquiring and releasing a resource from firmware and the parsing of information returned from the ibm,configure-connector rtas call. Additionally this exports the pSeries reconfiguration notifier chain so that it can be invoked when device tree updates are made. Signed-off-by: Nathan Fontenot <nfont@austin.ibm.com> Acked-by: Paul Mackerras <paulus@samba.org> Signed-off-by: Benjamin Herrenschmidt <benh@kernel.crashing.org>
1 parent 1496e89 commit ab519a0

File tree

4 files changed

+347
-2
lines changed

4 files changed

+347
-2
lines changed

arch/powerpc/include/asm/pSeries_reconfig.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
#ifdef CONFIG_PPC_PSERIES
1818
extern int pSeries_reconfig_notifier_register(struct notifier_block *);
1919
extern void pSeries_reconfig_notifier_unregister(struct notifier_block *);
20+
extern struct blocking_notifier_head pSeries_reconfig_chain;
2021
#else /* !CONFIG_PPC_PSERIES */
2122
static inline int pSeries_reconfig_notifier_register(struct notifier_block *nb)
2223
{

arch/powerpc/platforms/pseries/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ endif
88

99
obj-y := lpar.o hvCall.o nvram.o reconfig.o \
1010
setup.o iommu.o ras.o \
11-
firmware.o power.o
11+
firmware.o power.o dlpar.o
1212
obj-$(CONFIG_SMP) += smp.o
1313
obj-$(CONFIG_XICS) += xics.o
1414
obj-$(CONFIG_SCANLOG) += scanlog.o
Lines changed: 344 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,344 @@
1+
/*
2+
* Support for dynamic reconfiguration for PCI, Memory, and CPU
3+
* Hotplug and Dynamic Logical Partitioning on RPA platforms.
4+
*
5+
* Copyright (C) 2009 Nathan Fontenot
6+
* Copyright (C) 2009 IBM Corporation
7+
*
8+
* This program is free software; you can redistribute it and/or
9+
* modify it under the terms of the GNU General Public License version
10+
* 2 as published by the Free Software Foundation.
11+
*/
12+
13+
#include <linux/kernel.h>
14+
#include <linux/kref.h>
15+
#include <linux/notifier.h>
16+
#include <linux/proc_fs.h>
17+
#include <linux/spinlock.h>
18+
#include <linux/cpu.h>
19+
20+
#include <asm/prom.h>
21+
#include <asm/machdep.h>
22+
#include <asm/uaccess.h>
23+
#include <asm/rtas.h>
24+
#include <asm/pSeries_reconfig.h>
25+
26+
struct cc_workarea {
27+
u32 drc_index;
28+
u32 zero;
29+
u32 name_offset;
30+
u32 prop_length;
31+
u32 prop_offset;
32+
};
33+
34+
static void dlpar_free_cc_property(struct property *prop)
35+
{
36+
kfree(prop->name);
37+
kfree(prop->value);
38+
kfree(prop);
39+
}
40+
41+
static struct property *dlpar_parse_cc_property(struct cc_workarea *ccwa)
42+
{
43+
struct property *prop;
44+
char *name;
45+
char *value;
46+
47+
prop = kzalloc(sizeof(*prop), GFP_KERNEL);
48+
if (!prop)
49+
return NULL;
50+
51+
name = (char *)ccwa + ccwa->name_offset;
52+
prop->name = kstrdup(name, GFP_KERNEL);
53+
54+
prop->length = ccwa->prop_length;
55+
value = (char *)ccwa + ccwa->prop_offset;
56+
prop->value = kzalloc(prop->length, GFP_KERNEL);
57+
if (!prop->value) {
58+
dlpar_free_cc_property(prop);
59+
return NULL;
60+
}
61+
62+
memcpy(prop->value, value, prop->length);
63+
return prop;
64+
}
65+
66+
static struct device_node *dlpar_parse_cc_node(struct cc_workarea *ccwa)
67+
{
68+
struct device_node *dn;
69+
char *name;
70+
71+
dn = kzalloc(sizeof(*dn), GFP_KERNEL);
72+
if (!dn)
73+
return NULL;
74+
75+
/* The configure connector reported name does not contain a
76+
* preceeding '/', so we allocate a buffer large enough to
77+
* prepend this to the full_name.
78+
*/
79+
name = (char *)ccwa + ccwa->name_offset;
80+
dn->full_name = kmalloc(strlen(name) + 2, GFP_KERNEL);
81+
if (!dn->full_name) {
82+
kfree(dn);
83+
return NULL;
84+
}
85+
86+
sprintf(dn->full_name, "/%s", name);
87+
return dn;
88+
}
89+
90+
static void dlpar_free_one_cc_node(struct device_node *dn)
91+
{
92+
struct property *prop;
93+
94+
while (dn->properties) {
95+
prop = dn->properties;
96+
dn->properties = prop->next;
97+
dlpar_free_cc_property(prop);
98+
}
99+
100+
kfree(dn->full_name);
101+
kfree(dn);
102+
}
103+
104+
static void dlpar_free_cc_nodes(struct device_node *dn)
105+
{
106+
if (dn->child)
107+
dlpar_free_cc_nodes(dn->child);
108+
109+
if (dn->sibling)
110+
dlpar_free_cc_nodes(dn->sibling);
111+
112+
dlpar_free_one_cc_node(dn);
113+
}
114+
115+
#define NEXT_SIBLING 1
116+
#define NEXT_CHILD 2
117+
#define NEXT_PROPERTY 3
118+
#define PREV_PARENT 4
119+
#define MORE_MEMORY 5
120+
#define CALL_AGAIN -2
121+
#define ERR_CFG_USE -9003
122+
123+
struct device_node *dlpar_configure_connector(u32 drc_index)
124+
{
125+
struct device_node *dn;
126+
struct device_node *first_dn = NULL;
127+
struct device_node *last_dn = NULL;
128+
struct property *property;
129+
struct property *last_property = NULL;
130+
struct cc_workarea *ccwa;
131+
int cc_token;
132+
int rc;
133+
134+
cc_token = rtas_token("ibm,configure-connector");
135+
if (cc_token == RTAS_UNKNOWN_SERVICE)
136+
return NULL;
137+
138+
spin_lock(&rtas_data_buf_lock);
139+
ccwa = (struct cc_workarea *)&rtas_data_buf[0];
140+
ccwa->drc_index = drc_index;
141+
ccwa->zero = 0;
142+
143+
rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL);
144+
while (rc) {
145+
switch (rc) {
146+
case NEXT_SIBLING:
147+
dn = dlpar_parse_cc_node(ccwa);
148+
if (!dn)
149+
goto cc_error;
150+
151+
dn->parent = last_dn->parent;
152+
last_dn->sibling = dn;
153+
last_dn = dn;
154+
break;
155+
156+
case NEXT_CHILD:
157+
dn = dlpar_parse_cc_node(ccwa);
158+
if (!dn)
159+
goto cc_error;
160+
161+
if (!first_dn)
162+
first_dn = dn;
163+
else {
164+
dn->parent = last_dn;
165+
if (last_dn)
166+
last_dn->child = dn;
167+
}
168+
169+
last_dn = dn;
170+
break;
171+
172+
case NEXT_PROPERTY:
173+
property = dlpar_parse_cc_property(ccwa);
174+
if (!property)
175+
goto cc_error;
176+
177+
if (!last_dn->properties)
178+
last_dn->properties = property;
179+
else
180+
last_property->next = property;
181+
182+
last_property = property;
183+
break;
184+
185+
case PREV_PARENT:
186+
last_dn = last_dn->parent;
187+
break;
188+
189+
case CALL_AGAIN:
190+
break;
191+
192+
case MORE_MEMORY:
193+
case ERR_CFG_USE:
194+
default:
195+
printk(KERN_ERR "Unexpected Error (%d) "
196+
"returned from configure-connector\n", rc);
197+
goto cc_error;
198+
}
199+
200+
rc = rtas_call(cc_token, 2, 1, NULL, rtas_data_buf, NULL);
201+
}
202+
203+
spin_unlock(&rtas_data_buf_lock);
204+
return first_dn;
205+
206+
cc_error:
207+
if (first_dn)
208+
dlpar_free_cc_nodes(first_dn);
209+
spin_unlock(&rtas_data_buf_lock);
210+
return NULL;
211+
}
212+
213+
static struct device_node *derive_parent(const char *path)
214+
{
215+
struct device_node *parent;
216+
char *last_slash;
217+
218+
last_slash = strrchr(path, '/');
219+
if (last_slash == path) {
220+
parent = of_find_node_by_path("/");
221+
} else {
222+
char *parent_path;
223+
int parent_path_len = last_slash - path + 1;
224+
parent_path = kmalloc(parent_path_len, GFP_KERNEL);
225+
if (!parent_path)
226+
return NULL;
227+
228+
strlcpy(parent_path, path, parent_path_len);
229+
parent = of_find_node_by_path(parent_path);
230+
kfree(parent_path);
231+
}
232+
233+
return parent;
234+
}
235+
236+
int dlpar_attach_node(struct device_node *dn)
237+
{
238+
struct proc_dir_entry *ent;
239+
int rc;
240+
241+
of_node_set_flag(dn, OF_DYNAMIC);
242+
kref_init(&dn->kref);
243+
dn->parent = derive_parent(dn->full_name);
244+
if (!dn->parent)
245+
return -ENOMEM;
246+
247+
rc = blocking_notifier_call_chain(&pSeries_reconfig_chain,
248+
PSERIES_RECONFIG_ADD, dn);
249+
if (rc == NOTIFY_BAD) {
250+
printk(KERN_ERR "Failed to add device node %s\n",
251+
dn->full_name);
252+
return -ENOMEM; /* For now, safe to assume kmalloc failure */
253+
}
254+
255+
of_attach_node(dn);
256+
257+
#ifdef CONFIG_PROC_DEVICETREE
258+
ent = proc_mkdir(strrchr(dn->full_name, '/') + 1, dn->parent->pde);
259+
if (ent)
260+
proc_device_tree_add_node(dn, ent);
261+
#endif
262+
263+
of_node_put(dn->parent);
264+
return 0;
265+
}
266+
267+
int dlpar_detach_node(struct device_node *dn)
268+
{
269+
struct device_node *parent = dn->parent;
270+
struct property *prop = dn->properties;
271+
272+
#ifdef CONFIG_PROC_DEVICETREE
273+
while (prop) {
274+
remove_proc_entry(prop->name, dn->pde);
275+
prop = prop->next;
276+
}
277+
278+
if (dn->pde)
279+
remove_proc_entry(dn->pde->name, parent->pde);
280+
#endif
281+
282+
blocking_notifier_call_chain(&pSeries_reconfig_chain,
283+
PSERIES_RECONFIG_REMOVE, dn);
284+
of_detach_node(dn);
285+
of_node_put(dn); /* Must decrement the refcount */
286+
287+
return 0;
288+
}
289+
290+
#define DR_ENTITY_SENSE 9003
291+
#define DR_ENTITY_PRESENT 1
292+
#define DR_ENTITY_UNUSABLE 2
293+
#define ALLOCATION_STATE 9003
294+
#define ALLOC_UNUSABLE 0
295+
#define ALLOC_USABLE 1
296+
#define ISOLATION_STATE 9001
297+
#define ISOLATE 0
298+
#define UNISOLATE 1
299+
300+
int dlpar_acquire_drc(u32 drc_index)
301+
{
302+
int dr_status, rc;
303+
304+
rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
305+
DR_ENTITY_SENSE, drc_index);
306+
if (rc || dr_status != DR_ENTITY_UNUSABLE)
307+
return -1;
308+
309+
rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_USABLE);
310+
if (rc)
311+
return rc;
312+
313+
rc = rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
314+
if (rc) {
315+
rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
316+
return rc;
317+
}
318+
319+
return 0;
320+
}
321+
322+
int dlpar_release_drc(u32 drc_index)
323+
{
324+
int dr_status, rc;
325+
326+
rc = rtas_call(rtas_token("get-sensor-state"), 2, 2, &dr_status,
327+
DR_ENTITY_SENSE, drc_index);
328+
if (rc || dr_status != DR_ENTITY_PRESENT)
329+
return -1;
330+
331+
rc = rtas_set_indicator(ISOLATION_STATE, drc_index, ISOLATE);
332+
if (rc)
333+
return rc;
334+
335+
rc = rtas_set_indicator(ALLOCATION_STATE, drc_index, ALLOC_UNUSABLE);
336+
if (rc) {
337+
rtas_set_indicator(ISOLATION_STATE, drc_index, UNISOLATE);
338+
return rc;
339+
}
340+
341+
return 0;
342+
}
343+
344+

arch/powerpc/platforms/pseries/reconfig.c

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -96,7 +96,7 @@ static struct device_node *derive_parent(const char *path)
9696
return parent;
9797
}
9898

99-
static BLOCKING_NOTIFIER_HEAD(pSeries_reconfig_chain);
99+
BLOCKING_NOTIFIER_HEAD(pSeries_reconfig_chain);
100100

101101
int pSeries_reconfig_notifier_register(struct notifier_block *nb)
102102
{

0 commit comments

Comments
 (0)