Re: [PATCH 2/2] libfdt: Use faster algorithm for fdt_node_offset_by_phandle

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]



On Wed, Mar 14, 2018 at 02:41:04PM +0100, Mario Six wrote:
> fdt_node_offset_by_phandle will return the offset of a node pointed to
> by a given phandle.
> 
> The current implementation is wasteful: We iterate over all nodes (using
> fdt_next_node), call fdt_get_phandle on each one to read the phandle
> value (if it's there), compare it with the given phandle, and return the
> current node's offset if the values are equal.
> 
> fdt_get_phandle itself may iterate over the properties of each node
> twice (and usually will, since most nodes will have neither the
> "phandle" nor the "linux,phandle" property), and advancing to the next
> node via fdt_next_node will then iterate over all properties again.
> 
> Fix this by implementing a function fdt_node_offset_by_phandle_, which
> recursively walks the device tree, checks each encountered property for
> equality with either "phandle" or "linux,phandle", compares the
> found phandle's value with the given phandle, and returns the currently
> traversed node's offset if they're equal.
> 
> Since the function uses fdt_next_tag, it encounters each tag at most
> once.
> 
> Signed-off-by: Mario Six <mario.six@xxxxxxxx>

As with 1/2 I'm a bit dubious as to whether the increased code
complexity is worth it.  But, there's a more important problem..

> ---
>  libfdt/fdt_ro.c | 72 ++++++++++++++++++++++++++++++++++++++++++++++-----------
>  1 file changed, 58 insertions(+), 14 deletions(-)
> 
> diff --git a/libfdt/fdt_ro.c b/libfdt/fdt_ro.c
> index 7d049fb..51aedbb 100644
> --- a/libfdt/fdt_ro.c
> +++ b/libfdt/fdt_ro.c
> @@ -605,29 +605,73 @@ int fdt_node_offset_by_prop_value(const void *fdt, int startoffset,
>  	return offset; /* error from fdt_next_node() */
>  }
>  
> +static int fdt_node_offset_by_phandle_(const void *fdt, uint32_t phandle,
> +				       int *next_offset, int cur_node)
> +{
> +	uint32_t tag;
> +
> +	do {
> +		int offset;
> +		const struct fdt_property *prop;
> +		const fdt32_t *php = NULL;
> +		int res;
> +
> +		offset = *next_offset;
> +		tag = fdt_next_tag(fdt, offset, next_offset);
> +
> +		switch (tag) {
> +		case FDT_NOP:
> +			break;
> +		case FDT_END_NODE:
> +			if (*next_offset > 0)
> +				return -FDT_ERR_NOTFOUND;
> +			break;
> +		case FDT_BEGIN_NODE:
> +			res = fdt_node_offset_by_phandle_(fdt, phandle,
> +							  next_offset, offset);

Recursion isn't ok to use here.  Some environments in which libfdt
might run (like the kernel) have extremely constrained stack space.
That's why you'll find a number of libfdt functions that have rather
weird implementations that could be done much more simply with
recursion.

> +			if (res >= 0)
> +				return res;
> +			break;
> +		case FDT_END:
> +			if (*next_offset >= 0 ||
> +			    ((*next_offset == -FDT_ERR_TRUNCATED)))
> +				return -FDT_ERR_NOTFOUND;
> +			break;
> +		case FDT_PROP:
> +			if (!(prop = fdt_get_property_by_offset(fdt, offset, NULL)))
> +				return -FDT_ERR_INTERNAL;
> +
> +			if (fdt_string_eq_(fdt, fdt32_to_cpu(prop->nameoff),
> +					   "phandle", 7))
> +				php = (const fdt32_t *)prop->data;
> +
> +			if (fdt_string_eq_(fdt, fdt32_to_cpu(prop->nameoff),
> +					   "linux,phandle", 5 + 1 + 7))
> +				php = (const fdt32_t *)prop->data;
> +
> +			if (php && fdt32_to_cpu(*php) == phandle)
> +				return cur_node;
> +
> +			break;
> +		}
> +	} while (tag != FDT_END);
> +
> +	return -FDT_ERR_TRUNCATED;
> +}
> +
>  int fdt_node_offset_by_phandle(const void *fdt, uint32_t phandle)
>  {
> -	int offset;
> +	int res;
> +	int next_offset = 0;
>  
>  	if ((phandle == 0) || (phandle == -1))
>  		return -FDT_ERR_BADPHANDLE;
>  
>  	FDT_CHECK_HEADER(fdt);
>  
> -	/* FIXME: The algorithm here is pretty horrible: we
> -	 * potentially scan each property of a node in
> -	 * fdt_get_phandle(), then if that didn't find what
> -	 * we want, we scan over them again making our way to the next
> -	 * node.  Still it's the easiest to implement approach;
> -	 * performance can come later. */
> -	for (offset = fdt_next_node(fdt, -1, NULL);
> -	     offset >= 0;
> -	     offset = fdt_next_node(fdt, offset, NULL)) {
> -		if (fdt_get_phandle(fdt, offset) == phandle)
> -			return offset;
> -	}
> +	res = fdt_node_offset_by_phandle_(fdt, phandle, &next_offset, -1);
>  
> -	return offset; /* error from fdt_next_node() */
> +	return res;
>  }
>  
>  int fdt_stringlist_contains(const char *strlist, int listlen, const char *str)

-- 
David Gibson			| I'll have my music baroque, and my code
david AT gibson.dropbear.id.au	| minimalist, thank you.  NOT _the_ _other_
				| _way_ _around_!
http://www.ozlabs.org/~dgibson

Attachment: signature.asc
Description: PGP signature


[Index of Archives]     [Device Tree]     [Device Tree Spec]     [Linux Driver Backports]     [Video for Linux]     [Linux USB Devel]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Yosemite Backpacking]

  Powered by Linux