diff mbox series

[v3,3/3] powerpc/pseries/dlpar: Add device tree nodes for DLPAR IO add

Message ID 20240822025028.938332-3-haren@linux.ibm.com (mailing list archive)
State Accepted
Commit 02b98ff44a57c1376c5a92a8518fda5c82bb5a91
Headers show
Series [v3,1/3] powerpc/pseries: Use correct data types from pseries_hp_errorlog struct | expand

Checks

Context Check Description
snowpatch_ozlabs/github-powerpc_ppctests success Successfully ran 8 jobs.
snowpatch_ozlabs/github-powerpc_selftests success Successfully ran 8 jobs.
snowpatch_ozlabs/github-powerpc_sparse success Successfully ran 4 jobs.
snowpatch_ozlabs/github-powerpc_kernel_qemu success Successfully ran 21 jobs.
snowpatch_ozlabs/github-powerpc_clang success Successfully ran 5 jobs.

Commit Message

Haren Myneni Aug. 22, 2024, 2:50 a.m. UTC
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>
---
 arch/powerpc/platforms/pseries/dlpar.c | 130 +++++++++++++++++++++++++
 1 file changed, 130 insertions(+)

Comments

Michael Ellerman Aug. 28, 2024, 8:12 a.m. UTC | #1
Hi Haren,

One query below about the of_node refcounting.

Haren Myneni <haren@linux.ibm.com> writes:
> 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>
> ---
>  arch/powerpc/platforms/pseries/dlpar.c | 130 +++++++++++++++++++++++++
>  1 file changed, 130 insertions(+)
>
> diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c
> index 1b49b47c4a4f..6f0bc3ddbf85 100644
> --- a/arch/powerpc/platforms/pseries/dlpar.c
> +++ b/arch/powerpc/platforms/pseries/dlpar.c
...
> @@ -330,6 +345,118 @@ int dlpar_unisolate_drc(u32 drc_index)
>  	return 0;
>  }
>  
> +static struct device_node *
> +get_device_node_with_drc_index(u32 index)
> +{
> +	struct device_node *np = NULL;
> +	u32 node_index;
> +	int rc;
> +
> +	for_each_node_with_property(np, "ibm,my-drc-index") {
> +		rc = of_property_read_u32(np, "ibm,my-drc-index",
> +					     &node_index);
> +		if (rc) {
> +			pr_err("%s: %pOF: of_property_read_u32 %s: %d\n",
> +			       __func__, np, "ibm,my-drc-index", rc);
> +			of_node_put(np);
> +			return NULL;
> +		}
> +
> +		if (index == node_index)
> +			break;

Here we return with np's refcount elevated.

> +	}
> +
> +	return np;
> +}
...
> +
> +static int dlpar_hp_dt_add(u32 index)
> +{
> +	struct device_node *np, *nodes;
> +	struct of_changeset ocs;
> +	int rc;
> +
> +	/*
> +	 * Do not add device node(s) if already exists in the
> +	 * device tree.
> +	 */
> +	np = get_device_node_with_drc_index(index);
> +	if (np) {
> +		pr_err("%s: Adding device node for index (%d), but "
> +				"already exists in the device tree\n",
> +				__func__, index);
> +		rc = -EINVAL;
> +		goto out;
 
In the error case you drop the reference on np (at out).

> +	}

> +	np = get_device_node_with_drc_info(index);
> 
But in the success case np is reassigned, so the refcount is leaked.
I think that's unintentional, but I'm not 100% sure.

> +	if (!np)
> +		return -EIO;
> +
> +	/* Next, configure the connector. */
> +	nodes = dlpar_configure_connector(cpu_to_be32(index), np);
> +	if (!nodes) {
> +		rc = -EIO;
> +		goto out;
> +	}
> +
> +	/*
> +	 * Add the new nodes from dlpar_configure_connector() onto
> +	 * the device-tree.
> +	 */
> +	of_changeset_init(&ocs);
> +	rc = dlpar_changeset_attach_cc_nodes(&ocs, nodes);
> +
> +	if (!rc)
> +		rc = of_changeset_apply(&ocs);
> +	else
> +		dlpar_free_cc_nodes(nodes);
> +
> +	of_changeset_destroy(&ocs);
> +
> +out:
> +	of_node_put(np);
> +	return rc;
> +}
> +
>  static int changeset_detach_node_recursive(struct of_changeset *ocs,
>  					struct device_node *node)
>  {

cheers
Haren Myneni Aug. 29, 2024, 2:26 a.m. UTC | #2
On Wed, 2024-08-28 at 18:12 +1000, Michael Ellerman wrote:
> Hi Haren,
> 
> One query below about the of_node refcounting.
> 
> Haren Myneni <haren@linux.ibm.com> writes:
> > 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>
> > ---
> >  arch/powerpc/platforms/pseries/dlpar.c | 130
> > +++++++++++++++++++++++++
> >  1 file changed, 130 insertions(+)
> > 
> > diff --git a/arch/powerpc/platforms/pseries/dlpar.c
> > b/arch/powerpc/platforms/pseries/dlpar.c
> > index 1b49b47c4a4f..6f0bc3ddbf85 100644
> > --- a/arch/powerpc/platforms/pseries/dlpar.c
> > +++ b/arch/powerpc/platforms/pseries/dlpar.c
> ...
> > @@ -330,6 +345,118 @@ int dlpar_unisolate_drc(u32 drc_index)
> >  	return 0;
> >  }
> >  
> > +static struct device_node *
> > +get_device_node_with_drc_index(u32 index)
> > +{
> > +	struct device_node *np = NULL;
> > +	u32 node_index;
> > +	int rc;
> > +
> > +	for_each_node_with_property(np, "ibm,my-drc-index") {
> > +		rc = of_property_read_u32(np, "ibm,my-drc-index",
> > +					     &node_index);
> > +		if (rc) {
> > +			pr_err("%s: %pOF: of_property_read_u32 %s:
> > %d\n",
> > +			       __func__, np, "ibm,my-drc-index", rc);
> > +			of_node_put(np);
> > +			return NULL;
> > +		}
> > +
> > +		if (index == node_index)
> > +			break;
> 
> Here we return with np's refcount elevated.
> 
> > +	}
> > +
> > +	return np;
> > +}
> ...
> > +
> > +static int dlpar_hp_dt_add(u32 index)
> > +{
> > +	struct device_node *np, *nodes;
> > +	struct of_changeset ocs;
> > +	int rc;
> > +
> > +	/*
> > +	 * Do not add device node(s) if already exists in the
> > +	 * device tree.
> > +	 */
> > +	np = get_device_node_with_drc_index(index);
> > +	if (np) {
> > +		pr_err("%s: Adding device node for index (%d), but "
> > +				"already exists in the device tree\n",
> > +				__func__, index);
> > +		rc = -EINVAL;
> > +		goto out;
>  
> In the error case you drop the reference on np (at out).
> 
> > +	}
> > +	np = get_device_node_with_drc_info(index);
> > 
> But in the success case np is reassigned, so the refcount is leaked.
> I think that's unintentional, but I'm not 100% sure.

Michael, 

get_device_node_with_drc_index() holds the refcount only if the node is
already exists. In this case this refcount is dropped. 

if (np) {
                pr_err("%s: Adding device node for index (%d), but "
                                "already exists in the device tree\n",
                                __func__, index);
                rc = -EINVAL;
                goto out; --> drop refcount 
        }

Whereas failure from the get_device_node_with_drc_index() - can not
find the match node. means we do not hold the refcount and need to add
the node from get_device_node_with_drc_info()

I should add a comment regarding refcount to make it clear. will post
V4 patch with this comment.


Thanks
Haren

> cheers
Michael Ellerman Aug. 29, 2024, 4:10 a.m. UTC | #3
Haren Myneni <haren@linux.ibm.com> writes:
> On Wed, 2024-08-28 at 18:12 +1000, Michael Ellerman wrote:
>> Hi Haren,
>> 
>> One query below about the of_node refcounting.
>> 
>> Haren Myneni <haren@linux.ibm.com> writes:
>> > 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>
>> > ---
>> >  arch/powerpc/platforms/pseries/dlpar.c | 130
>> > +++++++++++++++++++++++++
>> >  1 file changed, 130 insertions(+)
>> > 
>> > diff --git a/arch/powerpc/platforms/pseries/dlpar.c
>> > b/arch/powerpc/platforms/pseries/dlpar.c
>> > index 1b49b47c4a4f..6f0bc3ddbf85 100644
>> > --- a/arch/powerpc/platforms/pseries/dlpar.c
>> > +++ b/arch/powerpc/platforms/pseries/dlpar.c
>> ...
>> > @@ -330,6 +345,118 @@ int dlpar_unisolate_drc(u32 drc_index)
>> >  	return 0;
>> >  }
>> >  
>> > +static struct device_node *
>> > +get_device_node_with_drc_index(u32 index)
>> > +{
>> > +	struct device_node *np = NULL;
>> > +	u32 node_index;
>> > +	int rc;
>> > +
>> > +	for_each_node_with_property(np, "ibm,my-drc-index") {
>> > +		rc = of_property_read_u32(np, "ibm,my-drc-index",
>> > +					     &node_index);
>> > +		if (rc) {
>> > +			pr_err("%s: %pOF: of_property_read_u32 %s:
>> > %d\n",
>> > +			       __func__, np, "ibm,my-drc-index", rc);
>> > +			of_node_put(np);
>> > +			return NULL;
>> > +		}
>> > +
>> > +		if (index == node_index)
>> > +			break;
>> 
>> Here we return with np's refcount elevated.
>> 
>> > +	}
>> > +
>> > +	return np;
>> > +}
>> ...
>> > +
>> > +static int dlpar_hp_dt_add(u32 index)
>> > +{
>> > +	struct device_node *np, *nodes;
>> > +	struct of_changeset ocs;
>> > +	int rc;
>> > +
>> > +	/*
>> > +	 * Do not add device node(s) if already exists in the
>> > +	 * device tree.
>> > +	 */
>> > +	np = get_device_node_with_drc_index(index);
>> > +	if (np) {
>> > +		pr_err("%s: Adding device node for index (%d), but "
>> > +				"already exists in the device tree\n",
>> > +				__func__, index);
>> > +		rc = -EINVAL;
>> > +		goto out;
>>  
>> In the error case you drop the reference on np (at out).
>> 
>> > +	}
>> > +	np = get_device_node_with_drc_info(index);
>> > 
>> But in the success case np is reassigned, so the refcount is leaked.
>> I think that's unintentional, but I'm not 100% sure.
>
> Michael, 
>
> get_device_node_with_drc_index() holds the refcount only if the node is
> already exists. In this case this refcount is dropped. 
>
> if (np) {
>                 pr_err("%s: Adding device node for index (%d), but "
>                                 "already exists in the device tree\n",
>                                 __func__, index);
>                 rc = -EINVAL;
>                 goto out; --> drop refcount 
>         }

Oh yep. I misread that as if (!np).

> Whereas failure from the get_device_node_with_drc_index() - can not
> find the match node. means we do not hold the refcount and need to add
> the node from get_device_node_with_drc_info()

Right.

> I should add a comment regarding refcount to make it clear. will post
> V4 patch with this comment.

It's probably fine as-is, I just needed to read it properly :)

cheers
diff mbox series

Patch

diff --git a/arch/powerpc/platforms/pseries/dlpar.c b/arch/powerpc/platforms/pseries/dlpar.c
index 1b49b47c4a4f..6f0bc3ddbf85 100644
--- a/arch/powerpc/platforms/pseries/dlpar.c
+++ b/arch/powerpc/platforms/pseries/dlpar.c
@@ -23,6 +23,7 @@ 
 #include <linux/uaccess.h>
 #include <asm/rtas.h>
 #include <asm/rtas-work-area.h>
+#include <asm/prom.h>
 
 static struct workqueue_struct *pseries_hp_wq;
 
@@ -264,6 +265,20 @@  int dlpar_detach_node(struct device_node *dn)
 
 	return 0;
 }
+static int dlpar_changeset_attach_cc_nodes(struct of_changeset *ocs,
+					struct device_node *dn)
+{
+	int rc;
+
+	rc = of_changeset_attach_node(ocs, dn);
+
+	if (!rc && dn->child)
+		rc = dlpar_changeset_attach_cc_nodes(ocs, dn->child);
+	if (!rc && dn->sibling)
+		rc = dlpar_changeset_attach_cc_nodes(ocs, dn->sibling);
+
+	return rc;
+}
 
 #define DR_ENTITY_SENSE		9003
 #define DR_ENTITY_PRESENT	1
@@ -330,6 +345,118 @@  int dlpar_unisolate_drc(u32 drc_index)
 	return 0;
 }
 
+static struct device_node *
+get_device_node_with_drc_index(u32 index)
+{
+	struct device_node *np = NULL;
+	u32 node_index;
+	int rc;
+
+	for_each_node_with_property(np, "ibm,my-drc-index") {
+		rc = of_property_read_u32(np, "ibm,my-drc-index",
+					     &node_index);
+		if (rc) {
+			pr_err("%s: %pOF: of_property_read_u32 %s: %d\n",
+			       __func__, np, "ibm,my-drc-index", rc);
+			of_node_put(np);
+			return NULL;
+		}
+
+		if (index == node_index)
+			break;
+	}
+
+	return np;
+}
+
+static struct device_node *
+get_device_node_with_drc_info(u32 index)
+{
+	struct device_node *np = NULL;
+	struct of_drc_info drc;
+	struct property *info;
+	const __be32 *value;
+	u32 node_index;
+	int i, j, count;
+
+	for_each_node_with_property(np, "ibm,drc-info") {
+		info = of_find_property(np, "ibm,drc-info", NULL);
+		if (info == NULL) {
+			/* XXX can this happen? */
+			of_node_put(np);
+			return NULL;
+		}
+		value = of_prop_next_u32(info, NULL, &count);
+		if (value == NULL)
+			continue;
+		value++;
+		for (i = 0; i < count; i++) {
+			if (of_read_drc_info_cell(&info, &value, &drc))
+				break;
+			if (index > drc.last_drc_index)
+				continue;
+			node_index = drc.drc_index_start;
+			for (j = 0; j < drc.num_sequential_elems; j++) {
+				if (index == node_index)
+					return np;
+				node_index += drc.sequential_inc;
+			}
+		}
+	}
+
+	return NULL;
+}
+
+static int dlpar_hp_dt_add(u32 index)
+{
+	struct device_node *np, *nodes;
+	struct of_changeset ocs;
+	int rc;
+
+	/*
+	 * Do not add device node(s) if already exists in the
+	 * device tree.
+	 */
+	np = get_device_node_with_drc_index(index);
+	if (np) {
+		pr_err("%s: Adding device node for index (%d), but "
+				"already exists in the device tree\n",
+				__func__, index);
+		rc = -EINVAL;
+		goto out;
+	}
+
+	np = get_device_node_with_drc_info(index);
+
+	if (!np)
+		return -EIO;
+
+	/* Next, configure the connector. */
+	nodes = dlpar_configure_connector(cpu_to_be32(index), np);
+	if (!nodes) {
+		rc = -EIO;
+		goto out;
+	}
+
+	/*
+	 * Add the new nodes from dlpar_configure_connector() onto
+	 * the device-tree.
+	 */
+	of_changeset_init(&ocs);
+	rc = dlpar_changeset_attach_cc_nodes(&ocs, nodes);
+
+	if (!rc)
+		rc = of_changeset_apply(&ocs);
+	else
+		dlpar_free_cc_nodes(nodes);
+
+	of_changeset_destroy(&ocs);
+
+out:
+	of_node_put(np);
+	return rc;
+}
+
 static int changeset_detach_node_recursive(struct of_changeset *ocs,
 					struct device_node *node)
 {
@@ -397,6 +524,9 @@  static int dlpar_hp_dt(struct pseries_hp_errorlog *phpe)
 	lock_device_hotplug();
 
 	switch (phpe->action) {
+	case PSERIES_HP_ELOG_ACTION_ADD:
+		rc = dlpar_hp_dt_add(drc_index);
+		break;
 	case PSERIES_HP_ELOG_ACTION_REMOVE:
 		rc = dlpar_hp_dt_remove(drc_index);
 		break;