diff mbox series

[v2,6/9] coresight: Store in-connections as well as out-connections

Message ID 20230310160610.742382-7-james.clark@arm.com (mailing list archive)
State New, archived
Headers show
Series coresight: Fix CTI module refcount leak by making it a helper device | expand

Commit Message

James Clark March 10, 2023, 4:06 p.m. UTC
This will allow CATU to get its associated ETR in a generic way where
currently the enable path has some hard coded searches which avoid
the need to store input connections.

Signed-off-by: James Clark <james.clark@arm.com>
---
 drivers/hwtracing/coresight/coresight-core.c  | 56 +++++++++++++++--
 .../hwtracing/coresight/coresight-platform.c  | 61 ++++++++++++++++---
 drivers/hwtracing/coresight/coresight-sysfs.c |  1 -
 include/linux/coresight.h                     | 25 ++++++++
 4 files changed, 130 insertions(+), 13 deletions(-)

Comments

Mao Jinlong March 14, 2023, 5:35 a.m. UTC | #1
On 3/11/2023 12:06 AM, James Clark wrote:
> This will allow CATU to get its associated ETR in a generic way where
> currently the enable path has some hard coded searches which avoid
> the need to store input connections.
>
> Signed-off-by: James Clark <james.clark@arm.com>
> ---
>   drivers/hwtracing/coresight/coresight-core.c  | 56 +++++++++++++++--
>   .../hwtracing/coresight/coresight-platform.c  | 61 ++++++++++++++++---
>   drivers/hwtracing/coresight/coresight-sysfs.c |  1 -
>   include/linux/coresight.h                     | 25 ++++++++
>   4 files changed, 130 insertions(+), 13 deletions(-)
>
> diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
> index f457914e445e..a8ba7493c09a 100644
> --- a/drivers/hwtracing/coresight/coresight-core.c
> +++ b/drivers/hwtracing/coresight/coresight-core.c
> @@ -59,6 +59,7 @@ const u32 coresight_barrier_pkt[4] = {0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fff
>   EXPORT_SYMBOL_GPL(coresight_barrier_pkt);
>   
>   static const struct cti_assoc_op *cti_assoc_ops;
> +static int coresight_fixup_inputs(struct coresight_device *csdev);
>   
>   ssize_t coresight_simple_show_pair(struct device *_dev,
>   			      struct device_attribute *attr, char *buf)
> @@ -1369,6 +1370,35 @@ static int coresight_fixup_orphan_conns(struct coresight_device *csdev)
>   			 csdev, coresight_orphan_match);
>   }
>   
> +/*
> + * Device connections are discovered before one/both devices have been created,
> + * so inputs must be added later.
> + */
> +static int coresight_fixup_inputs(struct coresight_device *csdev)
> +{
> +	int i, ret = 0;
> +	struct coresight_connection *out_conn;
> +	struct coresight_connection in_conn;
> +
> +	for (i = 0; i < csdev->pdata->nr_outconns; i++) {
> +		out_conn = &csdev->pdata->out_conns[i];
> +		if (!out_conn->remote_dev || !out_conn->remote_dev->pdata)
> +			continue;

Hi James,

If out_conn->remote_dev is null here,  the in_conn of 
out_conn->remote_dev->pdata will never be set.
For example, device A is connected to in_port 0 of device B. If device A 
is probed first, the in_conn of device
B will not be set.
Do we need to add Defer probe return here ? I tested with defer probe 
return, it works.

         for (i = 0; i < csdev->pdata->nr_outconns; i++) {
                 out_conn = &csdev->pdata->out_conns[i];
                 if (!out_conn->remote_dev || !out_conn->remote_dev->pdata)
-                       continue;
+                        return -EPROBE_DEFER;

Thanks
Jinlong Mao

> +
> +		/* Reverse local/remote relationships for inputs */
> +		in_conn.remote_dev = csdev;
> +		in_conn.remote_port = out_conn->port;
> +		in_conn.port = out_conn->remote_port;
> +		in_conn.remote_fwnode = csdev->dev.fwnode;
> +		ret = coresight_add_in_conn(out_conn->remote_dev->dev.parent,
> +					    out_conn->remote_dev->pdata,
> +					    &in_conn);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
>   
>   static int coresight_fixup_device_conns(struct coresight_device *csdev)
>   {
> @@ -1427,11 +1457,20 @@ static int coresight_remove_match(struct device *dev, void *data)
>   			 */
>   			fwnode_handle_put(conn->remote_fwnode);
>   			conn->remote_fwnode = NULL;
> +			conn->remote_dev = NULL;
> +			/* No need to continue */
> +			break;
> +		}
> +	}
> +	for (i = 0; i < iterator->pdata->nr_inconns; i++) {
> +		conn = &iterator->pdata->in_conns[i];
> +		if (csdev == conn->remote_dev) {
> +			conn->remote_fwnode = NULL;
> +			conn->remote_dev = NULL;
>   			/* No need to continue */
>   			break;
>   		}
>   	}
> -
>   	/*
>   	 * Returning '0' ensures that all known component on the
>   	 * bus will be checked.
> @@ -1552,21 +1591,28 @@ void coresight_release_platform_data(struct coresight_device *csdev,
>   
>   	for (i = 0; i < pdata->nr_outconns; i++) {
>   		/* If we have made the links, remove them now */
> -		if (csdev && conns[i].remote_dev)
> +		if (csdev && conns[i].remote_dev) {
>   			coresight_remove_links(csdev, &conns[i]);
> +			conns[i].remote_dev = NULL;
> +		}
> +
>   		/*
>   		 * Drop the refcount and clear the handle as this device
>   		 * is going away
>   		 */
>   		if (conns[i].remote_fwnode) {
>   			fwnode_handle_put(conns[i].remote_fwnode);
> -			pdata->out_conns[i].remote_fwnode = NULL;
> +			conns[i].remote_fwnode = NULL;
>   		}
>   	}
> +	for (i = 0; i < pdata->nr_inconns; i++) {
> +		pdata->in_conns[i].remote_dev = NULL;
> +		pdata->in_conns[i].remote_fwnode = NULL;
> +	}
> +
>   	if (csdev)
>   		coresight_remove_conns_sysfs_group(csdev);
>   }
> -
>   struct coresight_device *coresight_register(struct coresight_desc *desc)
>   {
>   	int ret;
> @@ -1659,6 +1705,8 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
>   	ret = coresight_create_conns_sysfs_group(csdev);
>   	if (!ret)
>   		ret = coresight_fixup_device_conns(csdev);
> +	if (!ret)
> +		ret = coresight_fixup_inputs(csdev);
>   	if (!ret)
>   		ret = coresight_fixup_orphan_conns(csdev);
>   
> diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c
> index 16553f7dde12..20e3351cbdc2 100644
> --- a/drivers/hwtracing/coresight/coresight-platform.c
> +++ b/drivers/hwtracing/coresight/coresight-platform.c
> @@ -20,8 +20,7 @@
>   
>   #include "coresight-priv.h"
>   /*
> - * coresight_alloc_conns: Allocate connections record for each output
> - * port from the device.
> + * coresight_alloc_conns: Allocate connections record for each input/output device.
>    */
>   static int coresight_alloc_conns(struct device *dev,
>   				 struct coresight_platform_data *pdata)
> @@ -33,7 +32,14 @@ static int coresight_alloc_conns(struct device *dev,
>   		if (!pdata->out_conns)
>   			return -ENOMEM;
>   	}
> -
> +	if (pdata->nr_inconns) {
> +		pdata->in_conns = devm_krealloc_array(dev, pdata->in_conns,
> +						      pdata->nr_inconns,
> +						      sizeof(*pdata->in_conns),
> +						      GFP_KERNEL | __GFP_ZERO);
> +		if (!pdata->in_conns)
> +			return -ENOMEM;
> +	}
>   	return 0;
>   }
>   
> @@ -79,6 +85,45 @@ int coresight_add_conn(struct device *dev,
>   }
>   EXPORT_SYMBOL_GPL(coresight_add_conn);
>   
> +/*
> + * Add a connection in the first free slot, or realloc
> + * if there is no space.
> + *
> + * Do nothing if the connection already exists because inputs are
> + * fixed up multiple times.
> + */
> +int coresight_add_in_conn(struct device *dev,
> +			  struct coresight_platform_data *pdata,
> +			  struct coresight_connection *conn)
> +{
> +	int ret;
> +	struct coresight_connection *free_conn = NULL;
> +	int i;
> +
> +	/* Search for a free slot or exit if a duplicate is found */
> +	if (pdata->in_conns) {
> +		for (i = 0; i < pdata->nr_inconns; ++i) {
> +			if (!free_conn && !pdata->in_conns[i].remote_fwnode)
> +				free_conn = &pdata->in_conns[i];
> +			if (pdata->in_conns[i].remote_fwnode ==
> +			    conn->remote_fwnode)
> +				return 0;
> +		}
> +	}
> +
> +	if (!free_conn) {
> +		pdata->nr_inconns++;
> +		ret = coresight_alloc_conns(dev, pdata);
> +		if (ret)
> +			return ret;
> +		free_conn = &pdata->in_conns[pdata->nr_inconns - 1];
> +	}
> +
> +	*free_conn = *conn;
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(coresight_add_in_conn);
> +
>   static struct device *
>   coresight_find_device_by_fwnode(struct fwnode_handle *fwnode)
>   {
> @@ -249,7 +294,7 @@ static int of_coresight_get_cpu(struct device *dev)
>   
>   /*
>    * of_coresight_parse_endpoint : Parse the given output endpoint @ep
> - * and fill the connection information in @conn
> + * and fill the connection information in @in_conn and @out_conn
>    *
>    * Parses the local port, remote device name and the remote port.
>    *
> @@ -333,14 +378,14 @@ static int of_get_coresight_platform_data(struct device *dev,
>   	/* Get the number of input and output port for this component */
>   	of_coresight_get_ports(node, &pdata->nr_inconns, &pdata->nr_outconns);
>   
> -	/* If there are no output connections, we are done */
> -	if (!pdata->nr_outconns)
> -		return 0;
> -
>   	ret = coresight_alloc_conns(dev, pdata);
>   	if (ret)
>   		return ret;
>   
> +	/* If there are no output connections, we are done */
> +	if (!pdata->nr_outconns)
> +		return 0;
> +
>   	parent = of_coresight_get_output_ports_node(node);
>   	/*
>   	 * If the DT uses obsoleted bindings, the ports are listed
> diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c
> index 3da9868d9237..2abf9639ac0f 100644
> --- a/drivers/hwtracing/coresight/coresight-sysfs.c
> +++ b/drivers/hwtracing/coresight/coresight-sysfs.c
> @@ -202,5 +202,4 @@ void coresight_remove_links(struct coresight_device *orig,
>   	devm_kfree(&orig->dev, conn->link->orig_name);
>   	devm_kfree(&orig->dev, conn->link);
>   	conn->link = NULL;
> -	conn->remote_dev = NULL;
>   }
> diff --git a/include/linux/coresight.h b/include/linux/coresight.h
> index 47fa58d6981d..fd268b24c761 100644
> --- a/include/linux/coresight.h
> +++ b/include/linux/coresight.h
> @@ -110,6 +110,7 @@ struct coresight_platform_data {
>   	int nr_inconns;
>   	int nr_outconns;
>   	struct coresight_connection *out_conns;
> +	struct coresight_connection *in_conns;
>   };
>   
>   /**
> @@ -177,6 +178,27 @@ struct coresight_desc {
>    *               @remote_fwnode once the remote's coresight_device has
>    *               been created.
>    * @link: Representation of the connection as a sysfs link.
> + *
> + * The full connection structure looks like this, where in_conns store references to
> + * the parent device in the same remote_dev member as output connections.
> + *
> + *       +-----------------------------+            +-----------------------------+
> + *       |coresight_device             |            |coresight_connection         |
> + *       |-----------------------------|            |-----------------------------|
> + *  ---->|                             |            |                             |
> + *  |    |                             |            |                  remote_dev*|------
> + *  |    |pdata->out_conns[nr_outconns]|----------->|                             |      |
> + *  |    |                             |            |                             |      |
> + *  |    +-----------------------------+            +-----------------------------+      |
> + *  |                                                                                    |
> + *  |    +-----------------------------+            +-----------------------------+      |
> + *  |    |coresight_connection         |            |coresight_device             |      |
> + *  |    |-----------------------------|            |------------------------------      |
> + *  |    |                             |            |                             |<-----
> + *  -----|remote_dev*                  |            |                             |
> + *       |                             |<-----------|pdata->in_conns[nr_inconns]  |
> + *       |                             |            |                             |
> + *       +-----------------------------+            +-----------------------------+
>    */
>   struct coresight_connection {
>   	int port;
> @@ -619,5 +641,8 @@ struct coresight_platform_data *coresight_get_platform_data(struct device *dev);
>   int coresight_add_conn(struct device *dev,
>   		       struct coresight_platform_data *pdata,
>   		       const struct coresight_connection *conn);
> +int coresight_add_in_conn(struct device *dev,
> +			  struct coresight_platform_data *pdata,
> +			  struct coresight_connection *conn);
>   
>   #endif		/* _LINUX_COREISGHT_H */
Suzuki K Poulose March 16, 2023, 8:23 p.m. UTC | #2
On 10/03/2023 16:06, James Clark wrote:
> This will allow CATU to get its associated ETR in a generic way where
> currently the enable path has some hard coded searches which avoid
> the need to store input connections.
> 
> Signed-off-by: James Clark <james.clark@arm.com>
> ---
>   drivers/hwtracing/coresight/coresight-core.c  | 56 +++++++++++++++--
>   .../hwtracing/coresight/coresight-platform.c  | 61 ++++++++++++++++---
>   drivers/hwtracing/coresight/coresight-sysfs.c |  1 -
>   include/linux/coresight.h                     | 25 ++++++++
>   4 files changed, 130 insertions(+), 13 deletions(-)
> 
> diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
> index f457914e445e..a8ba7493c09a 100644
> --- a/drivers/hwtracing/coresight/coresight-core.c
> +++ b/drivers/hwtracing/coresight/coresight-core.c
> @@ -59,6 +59,7 @@ const u32 coresight_barrier_pkt[4] = {0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fff
>   EXPORT_SYMBOL_GPL(coresight_barrier_pkt);
>   
>   static const struct cti_assoc_op *cti_assoc_ops;
> +static int coresight_fixup_inputs(struct coresight_device *csdev);
>   
>   ssize_t coresight_simple_show_pair(struct device *_dev,
>   			      struct device_attribute *attr, char *buf)
> @@ -1369,6 +1370,35 @@ static int coresight_fixup_orphan_conns(struct coresight_device *csdev)
>   			 csdev, coresight_orphan_match);
>   }
>   
> +/*
> + * Device connections are discovered before one/both devices have been created,
> + * so inputs must be added later.
> + */
> +static int coresight_fixup_inputs(struct coresight_device *csdev)
> +{
> +	int i, ret = 0;
> +	struct coresight_connection *out_conn;
> +	struct coresight_connection in_conn;
> +
> +	for (i = 0; i < csdev->pdata->nr_outconns; i++) {
> +		out_conn = &csdev->pdata->out_conns[i];
> +		if (!out_conn->remote_dev || !out_conn->remote_dev->pdata)
> +			continue;
> +
> +		/* Reverse local/remote relationships for inputs */
> +		in_conn.remote_dev = csdev;
> +		in_conn.remote_port = out_conn->port;
> +		in_conn.port = out_conn->remote_port;
> +		in_conn.remote_fwnode = csdev->dev.fwnode;
> +		ret = coresight_add_in_conn(out_conn->remote_dev->dev.parent,
> +					    out_conn->remote_dev->pdata,
> +					    &in_conn);
> +		if (ret)
> +			return ret;
> +	}
> +
> +	return 0;
> +}
>   
>   static int coresight_fixup_device_conns(struct coresight_device *csdev)
>   {
> @@ -1427,11 +1457,20 @@ static int coresight_remove_match(struct device *dev, void *data)

Removing the connection should be much simpler now with the inports
connection tracking.

i.e., coresight_remove_conns() need not iterate over all the devices on
the coresight bus. We had to do that to find all the devices connecting
to the removed device.

Instead with the in_conns we could readily get the csdev instance and
remove the connection from those devices.

Similarly for the out_conns can give you the remove device for removing
the in_conns on that device.

i.e,

static void coresight_remove_conns(csdev)
{
	struct coresight_device *remote;
	
	for (i = 0; i < csdev->pdata->nr_outconns; i++) {
		remote = csdev->data->out_conns[i].remote_dev;

		if (!remote)
			continue;

		for (j = 0; j < remote->pdata->nr_inconns; j++) {
			if (remote->pdata->in_conns[i].remote_dev ==  csdev)
			/* Clear in_conns[i] on remote */
		
		}
         }

	Similarly for in_conns
	for (i = 0; i < csdev->pdata->nr_inconns; i++) {
		remote = csdev->data->in_conns[i].remote_dev;

		if (!remote)
			continue;

		for (j = 0; j < remote->pdata->nr_outconns; j++) {
			if (remote->pdata->out_conns[i].remote_dev ==  csdev)
			/* Clear out_conns[i] on remote */
}


>   			 */
>   			fwnode_handle_put(conn->remote_fwnode);
>   			conn->remote_fwnode = NULL;
> +			conn->remote_dev = NULL;
> +			/* No need to continue */
> +			break;
> +		}
> +	}
> +	for (i = 0; i < iterator->pdata->nr_inconns; i++) {
> +		conn = &iterator->pdata->in_conns[i];
> +		if (csdev == conn->remote_dev) {
> +			conn->remote_fwnode = NULL;
> +			conn->remote_dev = NULL;
>   			/* No need to continue */
>   			break;
>   		}
>   	}
> -
>   	/*
>   	 * Returning '0' ensures that all known component on the
>   	 * bus will be checked.
> @@ -1552,21 +1591,28 @@ void coresight_release_platform_data(struct coresight_device *csdev,
>   
>   	for (i = 0; i < pdata->nr_outconns; i++) {
>   		/* If we have made the links, remove them now */
> -		if (csdev && conns[i].remote_dev)
> +		if (csdev && conns[i].remote_dev) {
>   			coresight_remove_links(csdev, &conns[i]);
> +			conns[i].remote_dev = NULL;
> +		}
> +
>   		/*
>   		 * Drop the refcount and clear the handle as this device
>   		 * is going away
>   		 */
>   		if (conns[i].remote_fwnode) {
>   			fwnode_handle_put(conns[i].remote_fwnode);
> -			pdata->out_conns[i].remote_fwnode = NULL;
> +			conns[i].remote_fwnode = NULL;
>   		}
>   	}
> +	for (i = 0; i < pdata->nr_inconns; i++) {
> +		pdata->in_conns[i].remote_dev = NULL;
> +		pdata->in_conns[i].remote_fwnode = NULL;
> +	}
> +
>   	if (csdev)
>   		coresight_remove_conns_sysfs_group(csdev);
>   }
> -
>   struct coresight_device *coresight_register(struct coresight_desc *desc)
>   {
>   	int ret;
> @@ -1659,6 +1705,8 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
>   	ret = coresight_create_conns_sysfs_group(csdev);
>   	if (!ret)
>   		ret = coresight_fixup_device_conns(csdev);
> +	if (!ret)
> +		ret = coresight_fixup_inputs(csdev);
Don't we also need to fixup the "input" connections on this
device ? It would be good move all of this connection stuff
into a wrapper function.

>   	if (!ret)
>   		ret = coresight_fixup_orphan_conns(csdev);
>   
> diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c
> index 16553f7dde12..20e3351cbdc2 100644
> --- a/drivers/hwtracing/coresight/coresight-platform.c
> +++ b/drivers/hwtracing/coresight/coresight-platform.c
> @@ -20,8 +20,7 @@
>   
>   #include "coresight-priv.h"
>   /*
> - * coresight_alloc_conns: Allocate connections record for each output
> - * port from the device.
> + * coresight_alloc_conns: Allocate connections record for each input/output device.
>    */
>   static int coresight_alloc_conns(struct device *dev,
>   				 struct coresight_platform_data *pdata)
> @@ -33,7 +32,14 @@ static int coresight_alloc_conns(struct device *dev,
>   		if (!pdata->out_conns)
>   			return -ENOMEM;
>   	}
> -
> +	if (pdata->nr_inconns) {
> +		pdata->in_conns = devm_krealloc_array(dev, pdata->in_conns,
> +						      pdata->nr_inconns,
> +						      sizeof(*pdata->in_conns),
> +						      GFP_KERNEL | __GFP_ZERO);
> +		if (!pdata->in_conns)
> +			return -ENOMEM;
> +	}

devm_krealloc_array() skips any allocation if the requested new_size <=
origin_size. So this should be fine for the out_conns, to not realloc
the space again unnecessarily.

>   	return 0;
>   }
>   
> @@ -79,6 +85,45 @@ int coresight_add_conn(struct device *dev,
>   }
>   EXPORT_SYMBOL_GPL(coresight_add_conn);

Should this be named as add_out_conn in the previous patch
just to make it explicit and easier to read.

>   
> +/*
> + * Add a connection in the first free slot, or realloc
> + * if there is no space.
> + *
> + * Do nothing if the connection already exists because inputs are
> + * fixed up multiple times.
> + */
> +int coresight_add_in_conn(struct device *dev,
> +			  struct coresight_platform_data *pdata,

minor nit: Is there any reason why this can't simply be:

    int coresight_add_in_conn(struct coresight_device *csdev,
			     struct coresight_connection *conn)
    {
		struct device *dev = csdev->dev.parent;
		struct coresight_platform_data *pdata = csdev->pdata;

> +			  struct coresight_connection *conn)


Suzuki


> +{
> +	int ret;
> +	struct coresight_connection *free_conn = NULL;
> +	int i;
> +
> +	/* Search for a free slot or exit if a duplicate is found */
> +	if (pdata->in_conns) {
> +		for (i = 0; i < pdata->nr_inconns; ++i) {
> +			if (!free_conn && !pdata->in_conns[i].remote_fwnode)
> +				free_conn = &pdata->in_conns[i];
> +			if (pdata->in_conns[i].remote_fwnode ==
> +			    conn->remote_fwnode)
> +				return 0;
> +		}
> +	}
> +
> +	if (!free_conn) {
> +		pdata->nr_inconns++;
> +		ret = coresight_alloc_conns(dev, pdata);
> +		if (ret)
> +			return ret;
> +		free_conn = &pdata->in_conns[pdata->nr_inconns - 1];
> +	}
> +
> +	*free_conn = *conn;
> +	return 0;
> +}
> +EXPORT_SYMBOL_GPL(coresight_add_in_conn);
> +
>   static struct device *
>   coresight_find_device_by_fwnode(struct fwnode_handle *fwnode)
>   {
> @@ -249,7 +294,7 @@ static int of_coresight_get_cpu(struct device *dev)
>   
>   /*
>    * of_coresight_parse_endpoint : Parse the given output endpoint @ep
> - * and fill the connection information in @conn
> + * and fill the connection information in @in_conn and @out_conn
>    *
>    * Parses the local port, remote device name and the remote port.
>    *
> @@ -333,14 +378,14 @@ static int of_get_coresight_platform_data(struct device *dev,
>   	/* Get the number of input and output port for this component */
>   	of_coresight_get_ports(node, &pdata->nr_inconns, &pdata->nr_outconns);
>   
> -	/* If there are no output connections, we are done */
> -	if (!pdata->nr_outconns)
> -		return 0;
> -
>   	ret = coresight_alloc_conns(dev, pdata);
>   	if (ret)
>   		return ret;
>   
> +	/* If there are no output connections, we are done */
> +	if (!pdata->nr_outconns)
> +		return 0;
> +
>   	parent = of_coresight_get_output_ports_node(node);
>   	/*
>   	 * If the DT uses obsoleted bindings, the ports are listed
> diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c
> index 3da9868d9237..2abf9639ac0f 100644
> --- a/drivers/hwtracing/coresight/coresight-sysfs.c
> +++ b/drivers/hwtracing/coresight/coresight-sysfs.c
> @@ -202,5 +202,4 @@ void coresight_remove_links(struct coresight_device *orig,
>   	devm_kfree(&orig->dev, conn->link->orig_name);
>   	devm_kfree(&orig->dev, conn->link);
>   	conn->link = NULL;
> -	conn->remote_dev = NULL;
>   }
> diff --git a/include/linux/coresight.h b/include/linux/coresight.h
> index 47fa58d6981d..fd268b24c761 100644
> --- a/include/linux/coresight.h
> +++ b/include/linux/coresight.h
> @@ -110,6 +110,7 @@ struct coresight_platform_data {
>   	int nr_inconns;
>   	int nr_outconns;
>   	struct coresight_connection *out_conns;
> +	struct coresight_connection *in_conns;

Please document the field above the structure.

Suzuki


>   };
>   
>   /**
> @@ -177,6 +178,27 @@ struct coresight_desc {
>    *               @remote_fwnode once the remote's coresight_device has
>    *               been created.
>    * @link: Representation of the connection as a sysfs link.
> + *
> + * The full connection structure looks like this, where in_conns store references to
> + * the parent device in the same remote_dev member as output connections.
> + *
> + *       +-----------------------------+            +-----------------------------+
> + *       |coresight_device             |            |coresight_connection         |
> + *       |-----------------------------|            |-----------------------------|
> + *  ---->|                             |            |                             |
> + *  |    |                             |            |                  remote_dev*|------
> + *  |    |pdata->out_conns[nr_outconns]|----------->|                             |      |
> + *  |    |                             |            |                             |      |
> + *  |    +-----------------------------+            +-----------------------------+      |
> + *  |                                                                                    |
> + *  |    +-----------------------------+            +-----------------------------+      |
> + *  |    |coresight_connection         |            |coresight_device             |      |
> + *  |    |-----------------------------|            |------------------------------      |
> + *  |    |                             |            |                             |<-----
> + *  -----|remote_dev*                  |            |                             |
> + *       |                             |<-----------|pdata->in_conns[nr_inconns]  |
> + *       |                             |            |                             |
> + *       +-----------------------------+            +-----------------------------+
>    */
>   struct coresight_connection {
>   	int port;
> @@ -619,5 +641,8 @@ struct coresight_platform_data *coresight_get_platform_data(struct device *dev);
>   int coresight_add_conn(struct device *dev,
>   		       struct coresight_platform_data *pdata,
>   		       const struct coresight_connection *conn);
> +int coresight_add_in_conn(struct device *dev,
> +			  struct coresight_platform_data *pdata,
> +			  struct coresight_connection *conn);
>   
>   #endif		/* _LINUX_COREISGHT_H */
Mike Leach March 21, 2023, 5:56 p.m. UTC | #3
Hi James

On Thu, 16 Mar 2023 at 20:23, Suzuki K Poulose <suzuki.poulose@arm.com> wrote:
>
> On 10/03/2023 16:06, James Clark wrote:
> > This will allow CATU to get its associated ETR in a generic way where
> > currently the enable path has some hard coded searches which avoid
> > the need to store input connections.
> >
> > Signed-off-by: James Clark <james.clark@arm.com>
> > ---
> >   drivers/hwtracing/coresight/coresight-core.c  | 56 +++++++++++++++--
> >   .../hwtracing/coresight/coresight-platform.c  | 61 ++++++++++++++++---
> >   drivers/hwtracing/coresight/coresight-sysfs.c |  1 -
> >   include/linux/coresight.h                     | 25 ++++++++
> >   4 files changed, 130 insertions(+), 13 deletions(-)
> >
> > diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
> > index f457914e445e..a8ba7493c09a 100644
> > --- a/drivers/hwtracing/coresight/coresight-core.c
> > +++ b/drivers/hwtracing/coresight/coresight-core.c
> > @@ -59,6 +59,7 @@ const u32 coresight_barrier_pkt[4] = {0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fff
> >   EXPORT_SYMBOL_GPL(coresight_barrier_pkt);
> >
> >   static const struct cti_assoc_op *cti_assoc_ops;
> > +static int coresight_fixup_inputs(struct coresight_device *csdev);
> >
> >   ssize_t coresight_simple_show_pair(struct device *_dev,
> >                             struct device_attribute *attr, char *buf)
> > @@ -1369,6 +1370,35 @@ static int coresight_fixup_orphan_conns(struct coresight_device *csdev)
> >                        csdev, coresight_orphan_match);
> >   }
> >
> > +/*
> > + * Device connections are discovered before one/both devices have been created,
> > + * so inputs must be added later.
> > + */
> > +static int coresight_fixup_inputs(struct coresight_device *csdev)
> > +{
> > +     int i, ret = 0;
> > +     struct coresight_connection *out_conn;
> > +     struct coresight_connection in_conn;
> > +
> > +     for (i = 0; i < csdev->pdata->nr_outconns; i++) {
> > +             out_conn = &csdev->pdata->out_conns[i];
> > +             if (!out_conn->remote_dev || !out_conn->remote_dev->pdata)
> > +                     continue;
> > +
> > +             /* Reverse local/remote relationships for inputs */
> > +             in_conn.remote_dev = csdev;
> > +             in_conn.remote_port = out_conn->port;
> > +             in_conn.port = out_conn->remote_port;
> > +             in_conn.remote_fwnode = csdev->dev.fwnode;
> > +             ret = coresight_add_in_conn(out_conn->remote_dev->dev.parent,
> > +                                         out_conn->remote_dev->pdata,
> > +                                         &in_conn);
> > +             if (ret)
> > +                     return ret;
> > +     }
> > +
> > +     return 0;
> > +}
> >
> >   static int coresight_fixup_device_conns(struct coresight_device *csdev)
> >   {
> > @@ -1427,11 +1457,20 @@ static int coresight_remove_match(struct device *dev, void *data)
>
> Removing the connection should be much simpler now with the inports
> connection tracking.
>
> i.e., coresight_remove_conns() need not iterate over all the devices on
> the coresight bus. We had to do that to find all the devices connecting
> to the removed device.
>
> Instead with the in_conns we could readily get the csdev instance and
> remove the connection from those devices.
>
> Similarly for the out_conns can give you the remove device for removing
> the in_conns on that device.
>
> i.e,
>
> static void coresight_remove_conns(csdev)
> {
>         struct coresight_device *remote;
>
>         for (i = 0; i < csdev->pdata->nr_outconns; i++) {
>                 remote = csdev->data->out_conns[i].remote_dev;
>
>                 if (!remote)
>                         continue;
>
>                 for (j = 0; j < remote->pdata->nr_inconns; j++) {
>                         if (remote->pdata->in_conns[i].remote_dev ==  csdev)
>                         /* Clear in_conns[i] on remote */
>
>                 }
>          }
>
>         Similarly for in_conns
>         for (i = 0; i < csdev->pdata->nr_inconns; i++) {
>                 remote = csdev->data->in_conns[i].remote_dev;
>
>                 if (!remote)
>                         continue;
>
>                 for (j = 0; j < remote->pdata->nr_outconns; j++) {
>                         if (remote->pdata->out_conns[i].remote_dev ==  csdev)
>                         /* Clear out_conns[i] on remote */
> }
>
>
> >                        */
> >                       fwnode_handle_put(conn->remote_fwnode);
> >                       conn->remote_fwnode = NULL;
> > +                     conn->remote_dev = NULL;
> > +                     /* No need to continue */
> > +                     break;
> > +             }
> > +     }
> > +     for (i = 0; i < iterator->pdata->nr_inconns; i++) {
> > +             conn = &iterator->pdata->in_conns[i];
> > +             if (csdev == conn->remote_dev) {
> > +                     conn->remote_fwnode = NULL;
> > +                     conn->remote_dev = NULL;
> >                       /* No need to continue */
> >                       break;
> >               }
> >       }
> > -
> >       /*
> >        * Returning '0' ensures that all known component on the
> >        * bus will be checked.
> > @@ -1552,21 +1591,28 @@ void coresight_release_platform_data(struct coresight_device *csdev,
> >
> >       for (i = 0; i < pdata->nr_outconns; i++) {
> >               /* If we have made the links, remove them now */
> > -             if (csdev && conns[i].remote_dev)
> > +             if (csdev && conns[i].remote_dev) {
> >                       coresight_remove_links(csdev, &conns[i]);
> > +                     conns[i].remote_dev = NULL;
> > +             }
> > +
> >               /*
> >                * Drop the refcount and clear the handle as this device
> >                * is going away
> >                */
> >               if (conns[i].remote_fwnode) {
> >                       fwnode_handle_put(conns[i].remote_fwnode);
> > -                     pdata->out_conns[i].remote_fwnode = NULL;
> > +                     conns[i].remote_fwnode = NULL;
> >               }
> >       }
> > +     for (i = 0; i < pdata->nr_inconns; i++) {
> > +             pdata->in_conns[i].remote_dev = NULL;
> > +             pdata->in_conns[i].remote_fwnode = NULL;
> > +     }
> > +
> >       if (csdev)
> >               coresight_remove_conns_sysfs_group(csdev);
> >   }
> > -
> >   struct coresight_device *coresight_register(struct coresight_desc *desc)
> >   {
> >       int ret;
> > @@ -1659,6 +1705,8 @@ struct coresight_device *coresight_register(struct coresight_desc *desc)
> >       ret = coresight_create_conns_sysfs_group(csdev);
> >       if (!ret)
> >               ret = coresight_fixup_device_conns(csdev);
> > +     if (!ret)
> > +             ret = coresight_fixup_inputs(csdev);
> Don't we also need to fixup the "input" connections on this
> device ? It would be good move all of this connection stuff
> into a wrapper function.
>
> >       if (!ret)
> >               ret = coresight_fixup_orphan_conns(csdev);
> >
> > diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c
> > index 16553f7dde12..20e3351cbdc2 100644
> > --- a/drivers/hwtracing/coresight/coresight-platform.c
> > +++ b/drivers/hwtracing/coresight/coresight-platform.c
> > @@ -20,8 +20,7 @@
> >
> >   #include "coresight-priv.h"
> >   /*
> > - * coresight_alloc_conns: Allocate connections record for each output
> > - * port from the device.
> > + * coresight_alloc_conns: Allocate connections record for each input/output device.
> >    */
> >   static int coresight_alloc_conns(struct device *dev,
> >                                struct coresight_platform_data *pdata)
> > @@ -33,7 +32,14 @@ static int coresight_alloc_conns(struct device *dev,
> >               if (!pdata->out_conns)
> >                       return -ENOMEM;
> >       }
> > -
> > +     if (pdata->nr_inconns) {
> > +             pdata->in_conns = devm_krealloc_array(dev, pdata->in_conns,
> > +                                                   pdata->nr_inconns,
> > +                                                   sizeof(*pdata->in_conns),
> > +                                                   GFP_KERNEL | __GFP_ZERO);
> > +             if (!pdata->in_conns)
> > +                     return -ENOMEM;
> > +     }
>
> devm_krealloc_array() skips any allocation if the requested new_size <=
> origin_size. So this should be fine for the out_conns, to not realloc
> the space again unnecessarily.
>
> >       return 0;
> >   }
> >
> > @@ -79,6 +85,45 @@ int coresight_add_conn(struct device *dev,
> >   }
> >   EXPORT_SYMBOL_GPL(coresight_add_conn);
>
> Should this be named as add_out_conn in the previous patch
> just to make it explicit and easier to read.
>
> >
> > +/*
> > + * Add a connection in the first free slot, or realloc
> > + * if there is no space.
> > + *
> > + * Do nothing if the connection already exists because inputs are
> > + * fixed up multiple times.
> > + */
> > +int coresight_add_in_conn(struct device *dev,
> > +                       struct coresight_platform_data *pdata,
>
> minor nit: Is there any reason why this can't simply be:
>
>     int coresight_add_in_conn(struct coresight_device *csdev,
>                              struct coresight_connection *conn)
>     {
>                 struct device *dev = csdev->dev.parent;
>                 struct coresight_platform_data *pdata = csdev->pdata;
>
> > +                       struct coresight_connection *conn)
>
>
> Suzuki
>
>
> > +{
> > +     int ret;
> > +     struct coresight_connection *free_conn = NULL;
> > +     int i;
> > +
> > +     /* Search for a free slot or exit if a duplicate is found */
> > +     if (pdata->in_conns) {
> > +             for (i = 0; i < pdata->nr_inconns; ++i) {
> > +                     if (!free_conn && !pdata->in_conns[i].remote_fwnode)
> > +                             free_conn = &pdata->in_conns[i];
> > +                     if (pdata->in_conns[i].remote_fwnode ==
> > +                         conn->remote_fwnode)
> > +                             return 0;
> > +             }
> > +     }
> > +

As with the out conns this assumes there can never be a match after a free slot.

Mike

> > +     if (!free_conn) {
> > +             pdata->nr_inconns++;
> > +             ret = coresight_alloc_conns(dev, pdata);
> > +             if (ret)
> > +                     return ret;
> > +             free_conn = &pdata->in_conns[pdata->nr_inconns - 1];
> > +     }
> > +
> > +     *free_conn = *conn;
> > +     return 0;
> > +}
> > +EXPORT_SYMBOL_GPL(coresight_add_in_conn);
> > +
> >   static struct device *
> >   coresight_find_device_by_fwnode(struct fwnode_handle *fwnode)
> >   {
> > @@ -249,7 +294,7 @@ static int of_coresight_get_cpu(struct device *dev)
> >
> >   /*
> >    * of_coresight_parse_endpoint : Parse the given output endpoint @ep
> > - * and fill the connection information in @conn
> > + * and fill the connection information in @in_conn and @out_conn
> >    *
> >    * Parses the local port, remote device name and the remote port.
> >    *
> > @@ -333,14 +378,14 @@ static int of_get_coresight_platform_data(struct device *dev,
> >       /* Get the number of input and output port for this component */
> >       of_coresight_get_ports(node, &pdata->nr_inconns, &pdata->nr_outconns);
> >
> > -     /* If there are no output connections, we are done */
> > -     if (!pdata->nr_outconns)
> > -             return 0;
> > -
> >       ret = coresight_alloc_conns(dev, pdata);
> >       if (ret)
> >               return ret;
> >
> > +     /* If there are no output connections, we are done */
> > +     if (!pdata->nr_outconns)
> > +             return 0;
> > +
> >       parent = of_coresight_get_output_ports_node(node);
> >       /*
> >        * If the DT uses obsoleted bindings, the ports are listed
> > diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c
> > index 3da9868d9237..2abf9639ac0f 100644
> > --- a/drivers/hwtracing/coresight/coresight-sysfs.c
> > +++ b/drivers/hwtracing/coresight/coresight-sysfs.c
> > @@ -202,5 +202,4 @@ void coresight_remove_links(struct coresight_device *orig,
> >       devm_kfree(&orig->dev, conn->link->orig_name);
> >       devm_kfree(&orig->dev, conn->link);
> >       conn->link = NULL;
> > -     conn->remote_dev = NULL;
> >   }
> > diff --git a/include/linux/coresight.h b/include/linux/coresight.h
> > index 47fa58d6981d..fd268b24c761 100644
> > --- a/include/linux/coresight.h
> > +++ b/include/linux/coresight.h
> > @@ -110,6 +110,7 @@ struct coresight_platform_data {
> >       int nr_inconns;
> >       int nr_outconns;
> >       struct coresight_connection *out_conns;
> > +     struct coresight_connection *in_conns;
>
> Please document the field above the structure.
>
> Suzuki
>
>
> >   };
> >
> >   /**
> > @@ -177,6 +178,27 @@ struct coresight_desc {
> >    *               @remote_fwnode once the remote's coresight_device has
> >    *               been created.
> >    * @link: Representation of the connection as a sysfs link.
> > + *
> > + * The full connection structure looks like this, where in_conns store references to
> > + * the parent device in the same remote_dev member as output connections.
> > + *
> > + *       +-----------------------------+            +-----------------------------+
> > + *       |coresight_device             |            |coresight_connection         |
> > + *       |-----------------------------|            |-----------------------------|
> > + *  ---->|                             |            |                             |
> > + *  |    |                             |            |                  remote_dev*|------
> > + *  |    |pdata->out_conns[nr_outconns]|----------->|                             |      |
> > + *  |    |                             |            |                             |      |
> > + *  |    +-----------------------------+            +-----------------------------+      |
> > + *  |                                                                                    |
> > + *  |    +-----------------------------+            +-----------------------------+      |
> > + *  |    |coresight_connection         |            |coresight_device             |      |
> > + *  |    |-----------------------------|            |------------------------------      |
> > + *  |    |                             |            |                             |<-----
> > + *  -----|remote_dev*                  |            |                             |
> > + *       |                             |<-----------|pdata->in_conns[nr_inconns]  |
> > + *       |                             |            |                             |
> > + *       +-----------------------------+            +-----------------------------+
> >    */
> >   struct coresight_connection {
> >       int port;
> > @@ -619,5 +641,8 @@ struct coresight_platform_data *coresight_get_platform_data(struct device *dev);
> >   int coresight_add_conn(struct device *dev,
> >                      struct coresight_platform_data *pdata,
> >                      const struct coresight_connection *conn);
> > +int coresight_add_in_conn(struct device *dev,
> > +                       struct coresight_platform_data *pdata,
> > +                       struct coresight_connection *conn);
> >
> >   #endif              /* _LINUX_COREISGHT_H */
>
James Clark March 29, 2023, 11:59 a.m. UTC | #4
On 16/03/2023 20:23, Suzuki K Poulose wrote:
> On 10/03/2023 16:06, James Clark wrote:
>> This will allow CATU to get its associated ETR in a generic way where
>> currently the enable path has some hard coded searches which avoid
>> the need to store input connections.
>>
>> Signed-off-by: James Clark <james.clark@arm.com>
>> ---
>>   drivers/hwtracing/coresight/coresight-core.c  | 56 +++++++++++++++--
>>   .../hwtracing/coresight/coresight-platform.c  | 61 ++++++++++++++++---
>>   drivers/hwtracing/coresight/coresight-sysfs.c |  1 -
>>   include/linux/coresight.h                     | 25 ++++++++
>>   4 files changed, 130 insertions(+), 13 deletions(-)
>>
>> diff --git a/drivers/hwtracing/coresight/coresight-core.c
>> b/drivers/hwtracing/coresight/coresight-core.c
>> index f457914e445e..a8ba7493c09a 100644
>> --- a/drivers/hwtracing/coresight/coresight-core.c
>> +++ b/drivers/hwtracing/coresight/coresight-core.c
>> @@ -59,6 +59,7 @@ const u32 coresight_barrier_pkt[4] = {0x7fffffff,
>> 0x7fffffff, 0x7fffffff, 0x7fff
>>   EXPORT_SYMBOL_GPL(coresight_barrier_pkt);
>>     static const struct cti_assoc_op *cti_assoc_ops;
>> +static int coresight_fixup_inputs(struct coresight_device *csdev);
>>     ssize_t coresight_simple_show_pair(struct device *_dev,
>>                     struct device_attribute *attr, char *buf)
>> @@ -1369,6 +1370,35 @@ static int coresight_fixup_orphan_conns(struct
>> coresight_device *csdev)
>>                csdev, coresight_orphan_match);
>>   }
>>   +/*
>> + * Device connections are discovered before one/both devices have
>> been created,
>> + * so inputs must be added later.
>> + */
>> +static int coresight_fixup_inputs(struct coresight_device *csdev)
>> +{
>> +    int i, ret = 0;
>> +    struct coresight_connection *out_conn;
>> +    struct coresight_connection in_conn;
>> +
>> +    for (i = 0; i < csdev->pdata->nr_outconns; i++) {
>> +        out_conn = &csdev->pdata->out_conns[i];
>> +        if (!out_conn->remote_dev || !out_conn->remote_dev->pdata)
>> +            continue;
>> +
>> +        /* Reverse local/remote relationships for inputs */
>> +        in_conn.remote_dev = csdev;
>> +        in_conn.remote_port = out_conn->port;
>> +        in_conn.port = out_conn->remote_port;
>> +        in_conn.remote_fwnode = csdev->dev.fwnode;
>> +        ret = coresight_add_in_conn(out_conn->remote_dev->dev.parent,
>> +                        out_conn->remote_dev->pdata,
>> +                        &in_conn);
>> +        if (ret)
>> +            return ret;
>> +    }
>> +
>> +    return 0;
>> +}
>>     static int coresight_fixup_device_conns(struct coresight_device
>> *csdev)
>>   {
>> @@ -1427,11 +1457,20 @@ static int coresight_remove_match(struct
>> device *dev, void *data)
> 
> Removing the connection should be much simpler now with the inports
> connection tracking.
> 
> i.e., coresight_remove_conns() need not iterate over all the devices on
> the coresight bus. We had to do that to find all the devices connecting
> to the removed device.

Done in v3

> 
> Instead with the in_conns we could readily get the csdev instance and
> remove the connection from those devices.
> 
> Similarly for the out_conns can give you the remove device for removing
> the in_conns on that device.
> 
> i.e,
> 
> static void coresight_remove_conns(csdev)
> {
>     struct coresight_device *remote;
>     
>     for (i = 0; i < csdev->pdata->nr_outconns; i++) {
>         remote = csdev->data->out_conns[i].remote_dev;
> 
>         if (!remote)
>             continue;
> 
>         for (j = 0; j < remote->pdata->nr_inconns; j++) {
>             if (remote->pdata->in_conns[i].remote_dev ==  csdev)
>             /* Clear in_conns[i] on remote */
>        
>         }
>         }
> 
>     Similarly for in_conns
>     for (i = 0; i < csdev->pdata->nr_inconns; i++) {
>         remote = csdev->data->in_conns[i].remote_dev;
> 
>         if (!remote)
>             continue;
> 
>         for (j = 0; j < remote->pdata->nr_outconns; j++) {
>             if (remote->pdata->out_conns[i].remote_dev ==  csdev)
>             /* Clear out_conns[i] on remote */
> }
> 
> 
>>                */
>>               fwnode_handle_put(conn->remote_fwnode);
>>               conn->remote_fwnode = NULL;
>> +            conn->remote_dev = NULL;
>> +            /* No need to continue */
>> +            break;
>> +        }
>> +    }
>> +    for (i = 0; i < iterator->pdata->nr_inconns; i++) {
>> +        conn = &iterator->pdata->in_conns[i];
>> +        if (csdev == conn->remote_dev) {
>> +            conn->remote_fwnode = NULL;
>> +            conn->remote_dev = NULL;
>>               /* No need to continue */
>>               break;
>>           }
>>       }
>> -
>>       /*
>>        * Returning '0' ensures that all known component on the
>>        * bus will be checked.
>> @@ -1552,21 +1591,28 @@ void coresight_release_platform_data(struct
>> coresight_device *csdev,
>>         for (i = 0; i < pdata->nr_outconns; i++) {
>>           /* If we have made the links, remove them now */
>> -        if (csdev && conns[i].remote_dev)
>> +        if (csdev && conns[i].remote_dev) {
>>               coresight_remove_links(csdev, &conns[i]);
>> +            conns[i].remote_dev = NULL;
>> +        }
>> +
>>           /*
>>            * Drop the refcount and clear the handle as this device
>>            * is going away
>>            */
>>           if (conns[i].remote_fwnode) {
>>               fwnode_handle_put(conns[i].remote_fwnode);
>> -            pdata->out_conns[i].remote_fwnode = NULL;
>> +            conns[i].remote_fwnode = NULL;
>>           }
>>       }
>> +    for (i = 0; i < pdata->nr_inconns; i++) {
>> +        pdata->in_conns[i].remote_dev = NULL;
>> +        pdata->in_conns[i].remote_fwnode = NULL;
>> +    }
>> +
>>       if (csdev)
>>           coresight_remove_conns_sysfs_group(csdev);
>>   }
>> -
>>   struct coresight_device *coresight_register(struct coresight_desc
>> *desc)
>>   {
>>       int ret;
>> @@ -1659,6 +1705,8 @@ struct coresight_device
>> *coresight_register(struct coresight_desc *desc)
>>       ret = coresight_create_conns_sysfs_group(csdev);
>>       if (!ret)
>>           ret = coresight_fixup_device_conns(csdev);
>> +    if (!ret)
>> +        ret = coresight_fixup_inputs(csdev);
> Don't we also need to fixup the "input" connections on this
> device ? 

> It would be good move all of this connection stuff
> into a wrapper function.
> 

Should be simplified now with coresight_fixup_device_conns() dropped.


>>       if (!ret)
>>           ret = coresight_fixup_orphan_conns(csdev);
>>   diff --git a/drivers/hwtracing/coresight/coresight-platform.c
>> b/drivers/hwtracing/coresight/coresight-platform.c
>> index 16553f7dde12..20e3351cbdc2 100644
>> --- a/drivers/hwtracing/coresight/coresight-platform.c
>> +++ b/drivers/hwtracing/coresight/coresight-platform.c
>> @@ -20,8 +20,7 @@
>>     #include "coresight-priv.h"
>>   /*
>> - * coresight_alloc_conns: Allocate connections record for each output
>> - * port from the device.
>> + * coresight_alloc_conns: Allocate connections record for each
>> input/output device.
>>    */
>>   static int coresight_alloc_conns(struct device *dev,
>>                    struct coresight_platform_data *pdata)
>> @@ -33,7 +32,14 @@ static int coresight_alloc_conns(struct device *dev,
>>           if (!pdata->out_conns)
>>               return -ENOMEM;
>>       }
>> -
>> +    if (pdata->nr_inconns) {
>> +        pdata->in_conns = devm_krealloc_array(dev, pdata->in_conns,
>> +                              pdata->nr_inconns,
>> +                              sizeof(*pdata->in_conns),
>> +                              GFP_KERNEL | __GFP_ZERO);
>> +        if (!pdata->in_conns)
>> +            return -ENOMEM;
>> +    }
> 
> devm_krealloc_array() skips any allocation if the requested new_size <=
> origin_size. So this should be fine for the out_conns, to not realloc
> the space again unnecessarily.

Now out_conns is contiguous so adding always allocates.

> 
>>       return 0;
>>   }
>>   @@ -79,6 +85,45 @@ int coresight_add_conn(struct device *dev,
>>   }
>>   EXPORT_SYMBOL_GPL(coresight_add_conn);
> 
> Should this be named as add_out_conn in the previous patch
> just to make it explicit and easier to read.

Done

> 
>>   +/*
>> + * Add a connection in the first free slot, or realloc
>> + * if there is no space.
>> + *
>> + * Do nothing if the connection already exists because inputs are
>> + * fixed up multiple times.
>> + */
>> +int coresight_add_in_conn(struct device *dev,
>> +              struct coresight_platform_data *pdata,
> 
> minor nit: Is there any reason why this can't simply be:
> 
>    int coresight_add_in_conn(struct coresight_device *csdev,
>                  struct coresight_connection *conn)
>    {
>         struct device *dev = csdev->dev.parent;
>         struct coresight_platform_data *pdata = csdev->pdata;
> 

No I can change it. I was going for consistency with
coresight_add_conn() which does need the device argument because it's
called both before and after the coresight_device is created. But yeah
it makes sense to simplify this one.

>> +              struct coresight_connection *conn)
> 
> 
> Suzuki
> 
> 
>> +{
>> +    int ret;
>> +    struct coresight_connection *free_conn = NULL;
>> +    int i;
>> +
>> +    /* Search for a free slot or exit if a duplicate is found */
>> +    if (pdata->in_conns) {
>> +        for (i = 0; i < pdata->nr_inconns; ++i) {
>> +            if (!free_conn && !pdata->in_conns[i].remote_fwnode)
>> +                free_conn = &pdata->in_conns[i];
>> +            if (pdata->in_conns[i].remote_fwnode ==
>> +                conn->remote_fwnode)
>> +                return 0;
>> +        }
>> +    }
>> +
>> +    if (!free_conn) {
>> +        pdata->nr_inconns++;
>> +        ret = coresight_alloc_conns(dev, pdata);
>> +        if (ret)
>> +            return ret;
>> +        free_conn = &pdata->in_conns[pdata->nr_inconns - 1];
>> +    }
>> +
>> +    *free_conn = *conn;
>> +    return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(coresight_add_in_conn);
>> +
>>   static struct device *
>>   coresight_find_device_by_fwnode(struct fwnode_handle *fwnode)
>>   {
>> @@ -249,7 +294,7 @@ static int of_coresight_get_cpu(struct device *dev)
>>     /*
>>    * of_coresight_parse_endpoint : Parse the given output endpoint @ep
>> - * and fill the connection information in @conn
>> + * and fill the connection information in @in_conn and @out_conn
>>    *
>>    * Parses the local port, remote device name and the remote port.
>>    *
>> @@ -333,14 +378,14 @@ static int of_get_coresight_platform_data(struct
>> device *dev,
>>       /* Get the number of input and output port for this component */
>>       of_coresight_get_ports(node, &pdata->nr_inconns,
>> &pdata->nr_outconns);
>>   -    /* If there are no output connections, we are done */
>> -    if (!pdata->nr_outconns)
>> -        return 0;
>> -
>>       ret = coresight_alloc_conns(dev, pdata);
>>       if (ret)
>>           return ret;
>>   +    /* If there are no output connections, we are done */
>> +    if (!pdata->nr_outconns)
>> +        return 0;
>> +
>>       parent = of_coresight_get_output_ports_node(node);
>>       /*
>>        * If the DT uses obsoleted bindings, the ports are listed
>> diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c
>> b/drivers/hwtracing/coresight/coresight-sysfs.c
>> index 3da9868d9237..2abf9639ac0f 100644
>> --- a/drivers/hwtracing/coresight/coresight-sysfs.c
>> +++ b/drivers/hwtracing/coresight/coresight-sysfs.c
>> @@ -202,5 +202,4 @@ void coresight_remove_links(struct
>> coresight_device *orig,
>>       devm_kfree(&orig->dev, conn->link->orig_name);
>>       devm_kfree(&orig->dev, conn->link);
>>       conn->link = NULL;
>> -    conn->remote_dev = NULL;
>>   }
>> diff --git a/include/linux/coresight.h b/include/linux/coresight.h
>> index 47fa58d6981d..fd268b24c761 100644
>> --- a/include/linux/coresight.h
>> +++ b/include/linux/coresight.h
>> @@ -110,6 +110,7 @@ struct coresight_platform_data {
>>       int nr_inconns;
>>       int nr_outconns;
>>       struct coresight_connection *out_conns;
>> +    struct coresight_connection *in_conns;
> 
> Please document the field above the structure.
> 
> Suzuki

Done

> 
> 
>>   };
>>     /**
>> @@ -177,6 +178,27 @@ struct coresight_desc {
>>    *               @remote_fwnode once the remote's coresight_device has
>>    *               been created.
>>    * @link: Representation of the connection as a sysfs link.
>> + *
>> + * The full connection structure looks like this, where in_conns
>> store references to
>> + * the parent device in the same remote_dev member as output
>> connections.
>> + *
>> + *       +-----------------------------+           
>> +-----------------------------+
>> + *       |coresight_device             |           
>> |coresight_connection         |
>> + *       |-----------------------------|           
>> |-----------------------------|
>> + *  ---->|                             |           
>> |                             |
>> + *  |    |                             |           
>> |                  remote_dev*|------
>> + *  |   
>> |pdata->out_conns[nr_outconns]|----------->|                             |      |
>> + *  |    |                             |           
>> |                             |      |
>> + *  |    +-----------------------------+           
>> +-----------------------------+      |
>> + * 
>> |                                                                                    |
>> + *  |    +-----------------------------+           
>> +-----------------------------+      |
>> + *  |    |coresight_connection         |           
>> |coresight_device             |      |
>> + *  |    |-----------------------------|           
>> |------------------------------      |
>> + *  |    |                             |           
>> |                             |<-----
>> + *  -----|remote_dev*                  |           
>> |                             |
>> + *       |                            
>> |<-----------|pdata->in_conns[nr_inconns]  |
>> + *       |                             |           
>> |                             |
>> + *       +-----------------------------+           
>> +-----------------------------+
>>    */
>>   struct coresight_connection {
>>       int port;
>> @@ -619,5 +641,8 @@ struct coresight_platform_data
>> *coresight_get_platform_data(struct device *dev);
>>   int coresight_add_conn(struct device *dev,
>>                  struct coresight_platform_data *pdata,
>>                  const struct coresight_connection *conn);
>> +int coresight_add_in_conn(struct device *dev,
>> +              struct coresight_platform_data *pdata,
>> +              struct coresight_connection *conn);
>>     #endif        /* _LINUX_COREISGHT_H */
>
James Clark March 29, 2023, 12:06 p.m. UTC | #5
On 14/03/2023 05:35, Jinlong Mao wrote:
> 
> On 3/11/2023 12:06 AM, James Clark wrote:
>> This will allow CATU to get its associated ETR in a generic way where
>> currently the enable path has some hard coded searches which avoid
>> the need to store input connections.
>>
>> Signed-off-by: James Clark <james.clark@arm.com>
>> ---
>>   drivers/hwtracing/coresight/coresight-core.c  | 56 +++++++++++++++--
>>   .../hwtracing/coresight/coresight-platform.c  | 61 ++++++++++++++++---
>>   drivers/hwtracing/coresight/coresight-sysfs.c |  1 -
>>   include/linux/coresight.h                     | 25 ++++++++
>>   4 files changed, 130 insertions(+), 13 deletions(-)
>>
>> diff --git a/drivers/hwtracing/coresight/coresight-core.c
>> b/drivers/hwtracing/coresight/coresight-core.c
>> index f457914e445e..a8ba7493c09a 100644
>> --- a/drivers/hwtracing/coresight/coresight-core.c
>> +++ b/drivers/hwtracing/coresight/coresight-core.c
>> @@ -59,6 +59,7 @@ const u32 coresight_barrier_pkt[4] = {0x7fffffff,
>> 0x7fffffff, 0x7fffffff, 0x7fff
>>   EXPORT_SYMBOL_GPL(coresight_barrier_pkt);
>>     static const struct cti_assoc_op *cti_assoc_ops;
>> +static int coresight_fixup_inputs(struct coresight_device *csdev);
>>     ssize_t coresight_simple_show_pair(struct device *_dev,
>>                     struct device_attribute *attr, char *buf)
>> @@ -1369,6 +1370,35 @@ static int coresight_fixup_orphan_conns(struct
>> coresight_device *csdev)
>>                csdev, coresight_orphan_match);
>>   }
>>   +/*
>> + * Device connections are discovered before one/both devices have
>> been created,
>> + * so inputs must be added later.
>> + */
>> +static int coresight_fixup_inputs(struct coresight_device *csdev)
>> +{
>> +    int i, ret = 0;
>> +    struct coresight_connection *out_conn;
>> +    struct coresight_connection in_conn;
>> +
>> +    for (i = 0; i < csdev->pdata->nr_outconns; i++) {
>> +        out_conn = &csdev->pdata->out_conns[i];
>> +        if (!out_conn->remote_dev || !out_conn->remote_dev->pdata)
>> +            continue;
> 
> Hi James,
> 
> If out_conn->remote_dev is null here,  the in_conn of
> out_conn->remote_dev->pdata will never be set.
> For example, device A is connected to in_port 0 of device B. If device A
> is probed first, the in_conn of device
> B will not be set.
> Do we need to add Defer probe return here ? I tested with defer probe
> return, it works.
> 
>         for (i = 0; i < csdev->pdata->nr_outconns; i++) {
>                 out_conn = &csdev->pdata->out_conns[i];
>                 if (!out_conn->remote_dev || !out_conn->remote_dev->pdata)
> -                       continue;
> +                        return -EPROBE_DEFER;
> 
> Thanks
> Jinlong Mao

I think you are right but I thought that EPROBE_DEFER was too big of a
change and that it might break something in some unexpected way.

In V3 I used the orphan mechanism for inputs in the same way as outputs
so the problem should be gone now but without having to defer loading.
diff mbox series

Patch

diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c
index f457914e445e..a8ba7493c09a 100644
--- a/drivers/hwtracing/coresight/coresight-core.c
+++ b/drivers/hwtracing/coresight/coresight-core.c
@@ -59,6 +59,7 @@  const u32 coresight_barrier_pkt[4] = {0x7fffffff, 0x7fffffff, 0x7fffffff, 0x7fff
 EXPORT_SYMBOL_GPL(coresight_barrier_pkt);
 
 static const struct cti_assoc_op *cti_assoc_ops;
+static int coresight_fixup_inputs(struct coresight_device *csdev);
 
 ssize_t coresight_simple_show_pair(struct device *_dev,
 			      struct device_attribute *attr, char *buf)
@@ -1369,6 +1370,35 @@  static int coresight_fixup_orphan_conns(struct coresight_device *csdev)
 			 csdev, coresight_orphan_match);
 }
 
+/*
+ * Device connections are discovered before one/both devices have been created,
+ * so inputs must be added later.
+ */
+static int coresight_fixup_inputs(struct coresight_device *csdev)
+{
+	int i, ret = 0;
+	struct coresight_connection *out_conn;
+	struct coresight_connection in_conn;
+
+	for (i = 0; i < csdev->pdata->nr_outconns; i++) {
+		out_conn = &csdev->pdata->out_conns[i];
+		if (!out_conn->remote_dev || !out_conn->remote_dev->pdata)
+			continue;
+
+		/* Reverse local/remote relationships for inputs */
+		in_conn.remote_dev = csdev;
+		in_conn.remote_port = out_conn->port;
+		in_conn.port = out_conn->remote_port;
+		in_conn.remote_fwnode = csdev->dev.fwnode;
+		ret = coresight_add_in_conn(out_conn->remote_dev->dev.parent,
+					    out_conn->remote_dev->pdata,
+					    &in_conn);
+		if (ret)
+			return ret;
+	}
+
+	return 0;
+}
 
 static int coresight_fixup_device_conns(struct coresight_device *csdev)
 {
@@ -1427,11 +1457,20 @@  static int coresight_remove_match(struct device *dev, void *data)
 			 */
 			fwnode_handle_put(conn->remote_fwnode);
 			conn->remote_fwnode = NULL;
+			conn->remote_dev = NULL;
+			/* No need to continue */
+			break;
+		}
+	}
+	for (i = 0; i < iterator->pdata->nr_inconns; i++) {
+		conn = &iterator->pdata->in_conns[i];
+		if (csdev == conn->remote_dev) {
+			conn->remote_fwnode = NULL;
+			conn->remote_dev = NULL;
 			/* No need to continue */
 			break;
 		}
 	}
-
 	/*
 	 * Returning '0' ensures that all known component on the
 	 * bus will be checked.
@@ -1552,21 +1591,28 @@  void coresight_release_platform_data(struct coresight_device *csdev,
 
 	for (i = 0; i < pdata->nr_outconns; i++) {
 		/* If we have made the links, remove them now */
-		if (csdev && conns[i].remote_dev)
+		if (csdev && conns[i].remote_dev) {
 			coresight_remove_links(csdev, &conns[i]);
+			conns[i].remote_dev = NULL;
+		}
+
 		/*
 		 * Drop the refcount and clear the handle as this device
 		 * is going away
 		 */
 		if (conns[i].remote_fwnode) {
 			fwnode_handle_put(conns[i].remote_fwnode);
-			pdata->out_conns[i].remote_fwnode = NULL;
+			conns[i].remote_fwnode = NULL;
 		}
 	}
+	for (i = 0; i < pdata->nr_inconns; i++) {
+		pdata->in_conns[i].remote_dev = NULL;
+		pdata->in_conns[i].remote_fwnode = NULL;
+	}
+
 	if (csdev)
 		coresight_remove_conns_sysfs_group(csdev);
 }
-
 struct coresight_device *coresight_register(struct coresight_desc *desc)
 {
 	int ret;
@@ -1659,6 +1705,8 @@  struct coresight_device *coresight_register(struct coresight_desc *desc)
 	ret = coresight_create_conns_sysfs_group(csdev);
 	if (!ret)
 		ret = coresight_fixup_device_conns(csdev);
+	if (!ret)
+		ret = coresight_fixup_inputs(csdev);
 	if (!ret)
 		ret = coresight_fixup_orphan_conns(csdev);
 
diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c
index 16553f7dde12..20e3351cbdc2 100644
--- a/drivers/hwtracing/coresight/coresight-platform.c
+++ b/drivers/hwtracing/coresight/coresight-platform.c
@@ -20,8 +20,7 @@ 
 
 #include "coresight-priv.h"
 /*
- * coresight_alloc_conns: Allocate connections record for each output
- * port from the device.
+ * coresight_alloc_conns: Allocate connections record for each input/output device.
  */
 static int coresight_alloc_conns(struct device *dev,
 				 struct coresight_platform_data *pdata)
@@ -33,7 +32,14 @@  static int coresight_alloc_conns(struct device *dev,
 		if (!pdata->out_conns)
 			return -ENOMEM;
 	}
-
+	if (pdata->nr_inconns) {
+		pdata->in_conns = devm_krealloc_array(dev, pdata->in_conns,
+						      pdata->nr_inconns,
+						      sizeof(*pdata->in_conns),
+						      GFP_KERNEL | __GFP_ZERO);
+		if (!pdata->in_conns)
+			return -ENOMEM;
+	}
 	return 0;
 }
 
@@ -79,6 +85,45 @@  int coresight_add_conn(struct device *dev,
 }
 EXPORT_SYMBOL_GPL(coresight_add_conn);
 
+/*
+ * Add a connection in the first free slot, or realloc
+ * if there is no space.
+ *
+ * Do nothing if the connection already exists because inputs are
+ * fixed up multiple times.
+ */
+int coresight_add_in_conn(struct device *dev,
+			  struct coresight_platform_data *pdata,
+			  struct coresight_connection *conn)
+{
+	int ret;
+	struct coresight_connection *free_conn = NULL;
+	int i;
+
+	/* Search for a free slot or exit if a duplicate is found */
+	if (pdata->in_conns) {
+		for (i = 0; i < pdata->nr_inconns; ++i) {
+			if (!free_conn && !pdata->in_conns[i].remote_fwnode)
+				free_conn = &pdata->in_conns[i];
+			if (pdata->in_conns[i].remote_fwnode ==
+			    conn->remote_fwnode)
+				return 0;
+		}
+	}
+
+	if (!free_conn) {
+		pdata->nr_inconns++;
+		ret = coresight_alloc_conns(dev, pdata);
+		if (ret)
+			return ret;
+		free_conn = &pdata->in_conns[pdata->nr_inconns - 1];
+	}
+
+	*free_conn = *conn;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(coresight_add_in_conn);
+
 static struct device *
 coresight_find_device_by_fwnode(struct fwnode_handle *fwnode)
 {
@@ -249,7 +294,7 @@  static int of_coresight_get_cpu(struct device *dev)
 
 /*
  * of_coresight_parse_endpoint : Parse the given output endpoint @ep
- * and fill the connection information in @conn
+ * and fill the connection information in @in_conn and @out_conn
  *
  * Parses the local port, remote device name and the remote port.
  *
@@ -333,14 +378,14 @@  static int of_get_coresight_platform_data(struct device *dev,
 	/* Get the number of input and output port for this component */
 	of_coresight_get_ports(node, &pdata->nr_inconns, &pdata->nr_outconns);
 
-	/* If there are no output connections, we are done */
-	if (!pdata->nr_outconns)
-		return 0;
-
 	ret = coresight_alloc_conns(dev, pdata);
 	if (ret)
 		return ret;
 
+	/* If there are no output connections, we are done */
+	if (!pdata->nr_outconns)
+		return 0;
+
 	parent = of_coresight_get_output_ports_node(node);
 	/*
 	 * If the DT uses obsoleted bindings, the ports are listed
diff --git a/drivers/hwtracing/coresight/coresight-sysfs.c b/drivers/hwtracing/coresight/coresight-sysfs.c
index 3da9868d9237..2abf9639ac0f 100644
--- a/drivers/hwtracing/coresight/coresight-sysfs.c
+++ b/drivers/hwtracing/coresight/coresight-sysfs.c
@@ -202,5 +202,4 @@  void coresight_remove_links(struct coresight_device *orig,
 	devm_kfree(&orig->dev, conn->link->orig_name);
 	devm_kfree(&orig->dev, conn->link);
 	conn->link = NULL;
-	conn->remote_dev = NULL;
 }
diff --git a/include/linux/coresight.h b/include/linux/coresight.h
index 47fa58d6981d..fd268b24c761 100644
--- a/include/linux/coresight.h
+++ b/include/linux/coresight.h
@@ -110,6 +110,7 @@  struct coresight_platform_data {
 	int nr_inconns;
 	int nr_outconns;
 	struct coresight_connection *out_conns;
+	struct coresight_connection *in_conns;
 };
 
 /**
@@ -177,6 +178,27 @@  struct coresight_desc {
  *               @remote_fwnode once the remote's coresight_device has
  *               been created.
  * @link: Representation of the connection as a sysfs link.
+ *
+ * The full connection structure looks like this, where in_conns store references to
+ * the parent device in the same remote_dev member as output connections.
+ *
+ *       +-----------------------------+            +-----------------------------+
+ *       |coresight_device             |            |coresight_connection         |
+ *       |-----------------------------|            |-----------------------------|
+ *  ---->|                             |            |                             |
+ *  |    |                             |            |                  remote_dev*|------
+ *  |    |pdata->out_conns[nr_outconns]|----------->|                             |      |
+ *  |    |                             |            |                             |      |
+ *  |    +-----------------------------+            +-----------------------------+      |
+ *  |                                                                                    |
+ *  |    +-----------------------------+            +-----------------------------+      |
+ *  |    |coresight_connection         |            |coresight_device             |      |
+ *  |    |-----------------------------|            |------------------------------      |
+ *  |    |                             |            |                             |<-----
+ *  -----|remote_dev*                  |            |                             |
+ *       |                             |<-----------|pdata->in_conns[nr_inconns]  |
+ *       |                             |            |                             |
+ *       +-----------------------------+            +-----------------------------+
  */
 struct coresight_connection {
 	int port;
@@ -619,5 +641,8 @@  struct coresight_platform_data *coresight_get_platform_data(struct device *dev);
 int coresight_add_conn(struct device *dev,
 		       struct coresight_platform_data *pdata,
 		       const struct coresight_connection *conn);
+int coresight_add_in_conn(struct device *dev,
+			  struct coresight_platform_data *pdata,
+			  struct coresight_connection *conn);
 
 #endif		/* _LINUX_COREISGHT_H */