Skip to content

Commit 02b98ff

Browse files
hmynenimpe
authored andcommitted
powerpc/pseries/dlpar: Add device tree nodes for DLPAR IO add
In the powerpc-pseries specific implementation, the IO hotplug event is handled in the user space (drmgr tool). For the DLPAR IO ADD, the corresponding device tree nodes and properties will be added to the device tree after the device enable. The user space (drmgr tool) uses configure_connector RTAS call with the DRC index to retrieve the device nodes and updates the device tree by writing to /proc/ppc64/ofdt. Under system lockdown, /dev/mem access to allocate buffers for configure_connector RTAS call is restricted which means the user space can not issue this RTAS call and also can not access to /proc/ppc64/ofdt. The pseries implementation need user interaction to power-on and add device to the slot during the ADD event handling. So adds complexity if the complete hotplug ADD event handling moved to the kernel. To overcome /dev/mem access restriction, this patch extends the /sys/kernel/dlpar interface and provides ‘dt add index <drc_index>’ to the user space. The drmgr tool uses this interface to update the device tree whenever the device is added. This interface retrieves device tree nodes for the corresponding DRC index using the configure_connector RTAS call and adds new device nodes / properties to the device tree. Signed-off-by: Scott Cheloha <cheloha@linux.ibm.com> Signed-off-by: Haren Myneni <haren@linux.ibm.com> Signed-off-by: Michael Ellerman <mpe@ellerman.id.au> Link: https://msgid.link/20240822025028.938332-3-haren@linux.ibm.com
1 parent 17a5117 commit 02b98ff

File tree

1 file changed

+130
-0
lines changed
  • arch/powerpc/platforms/pseries

1 file changed

+130
-0
lines changed

arch/powerpc/platforms/pseries/dlpar.c

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@
2323
#include <linux/uaccess.h>
2424
#include <asm/rtas.h>
2525
#include <asm/rtas-work-area.h>
26+
#include <asm/prom.h>
2627

2728
static struct workqueue_struct *pseries_hp_wq;
2829

@@ -261,6 +262,20 @@ int dlpar_detach_node(struct device_node *dn)
261262

262263
return 0;
263264
}
265+
static int dlpar_changeset_attach_cc_nodes(struct of_changeset *ocs,
266+
struct device_node *dn)
267+
{
268+
int rc;
269+
270+
rc = of_changeset_attach_node(ocs, dn);
271+
272+
if (!rc && dn->child)
273+
rc = dlpar_changeset_attach_cc_nodes(ocs, dn->child);
274+
if (!rc && dn->sibling)
275+
rc = dlpar_changeset_attach_cc_nodes(ocs, dn->sibling);
276+
277+
return rc;
278+
}
264279

265280
#define DR_ENTITY_SENSE 9003
266281
#define DR_ENTITY_PRESENT 1
@@ -327,6 +342,118 @@ int dlpar_unisolate_drc(u32 drc_index)
327342
return 0;
328343
}
329344

345+
static struct device_node *
346+
get_device_node_with_drc_index(u32 index)
347+
{
348+
struct device_node *np = NULL;
349+
u32 node_index;
350+
int rc;
351+
352+
for_each_node_with_property(np, "ibm,my-drc-index") {
353+
rc = of_property_read_u32(np, "ibm,my-drc-index",
354+
&node_index);
355+
if (rc) {
356+
pr_err("%s: %pOF: of_property_read_u32 %s: %d\n",
357+
__func__, np, "ibm,my-drc-index", rc);
358+
of_node_put(np);
359+
return NULL;
360+
}
361+
362+
if (index == node_index)
363+
break;
364+
}
365+
366+
return np;
367+
}
368+
369+
static struct device_node *
370+
get_device_node_with_drc_info(u32 index)
371+
{
372+
struct device_node *np = NULL;
373+
struct of_drc_info drc;
374+
struct property *info;
375+
const __be32 *value;
376+
u32 node_index;
377+
int i, j, count;
378+
379+
for_each_node_with_property(np, "ibm,drc-info") {
380+
info = of_find_property(np, "ibm,drc-info", NULL);
381+
if (info == NULL) {
382+
/* XXX can this happen? */
383+
of_node_put(np);
384+
return NULL;
385+
}
386+
value = of_prop_next_u32(info, NULL, &count);
387+
if (value == NULL)
388+
continue;
389+
value++;
390+
for (i = 0; i < count; i++) {
391+
if (of_read_drc_info_cell(&info, &value, &drc))
392+
break;
393+
if (index > drc.last_drc_index)
394+
continue;
395+
node_index = drc.drc_index_start;
396+
for (j = 0; j < drc.num_sequential_elems; j++) {
397+
if (index == node_index)
398+
return np;
399+
node_index += drc.sequential_inc;
400+
}
401+
}
402+
}
403+
404+
return NULL;
405+
}
406+
407+
static int dlpar_hp_dt_add(u32 index)
408+
{
409+
struct device_node *np, *nodes;
410+
struct of_changeset ocs;
411+
int rc;
412+
413+
/*
414+
* Do not add device node(s) if already exists in the
415+
* device tree.
416+
*/
417+
np = get_device_node_with_drc_index(index);
418+
if (np) {
419+
pr_err("%s: Adding device node for index (%d), but "
420+
"already exists in the device tree\n",
421+
__func__, index);
422+
rc = -EINVAL;
423+
goto out;
424+
}
425+
426+
np = get_device_node_with_drc_info(index);
427+
428+
if (!np)
429+
return -EIO;
430+
431+
/* Next, configure the connector. */
432+
nodes = dlpar_configure_connector(cpu_to_be32(index), np);
433+
if (!nodes) {
434+
rc = -EIO;
435+
goto out;
436+
}
437+
438+
/*
439+
* Add the new nodes from dlpar_configure_connector() onto
440+
* the device-tree.
441+
*/
442+
of_changeset_init(&ocs);
443+
rc = dlpar_changeset_attach_cc_nodes(&ocs, nodes);
444+
445+
if (!rc)
446+
rc = of_changeset_apply(&ocs);
447+
else
448+
dlpar_free_cc_nodes(nodes);
449+
450+
of_changeset_destroy(&ocs);
451+
452+
out:
453+
of_node_put(np);
454+
return rc;
455+
}
456+
330457
static int changeset_detach_node_recursive(struct of_changeset *ocs,
331458
struct device_node *node)
332459
{
@@ -394,6 +521,9 @@ static int dlpar_hp_dt(struct pseries_hp_errorlog *phpe)
394521
lock_device_hotplug();
395522

396523
switch (phpe->action) {
524+
case PSERIES_HP_ELOG_ACTION_ADD:
525+
rc = dlpar_hp_dt_add(drc_index);
526+
break;
397527
case PSERIES_HP_ELOG_ACTION_REMOVE:
398528
rc = dlpar_hp_dt_remove(drc_index);
399529
break;

0 commit comments

Comments
 (0)