[PATCH 1/2] dm: core: Fix iteration over driver_info records

Paul Barker paul.barker at sancloud.com
Tue Oct 25 14:59:24 CEST 2022


On 15/10/2022 18:53, Simon Glass wrote:
> Hi Paul,
> 
> On Sat, 15 Oct 2022 at 03:19, Paul Barker <paul.barker at sancloud.com> wrote:
>>
>> We should only perform additional iteration steps when needed to
>> initialize the parent of a device. Other binding errors (such as a
>> missing driver) should not lead to additional iteration steps.
>>
>> Unnecessary iteration steps can cause issues when memory is tightly
>> constrained (such as in the TPL/SPL) since device_bind_by_name()
>> unconditionally allocates memory for a struct udevice. On the SanCloud
>> BBE this led to boot failure caused by memory exhaustion in the SPL
>> when booting from SPI flash.
>>
>> Signed-off-by: Paul Barker <paul.barker at sancloud.com>
>> ---
>>  drivers/core/lists.c | 17 ++++++-----------
>>  1 file changed, 6 insertions(+), 11 deletions(-)
> 
> Are you able to construct a test for this? See test/dm/core.c or test-fdt.c

I can't see how to construct a test case for this issue. What I observed
was the SPL crashing during the pre-relocation lists_bind_drivers() call
due to exhaustion of the available memory. On the AM335x SoC this is 64k
of on-board SRAM which is available before main DRAM is initialised. I
can't see how to reproduce this issue within the u-boot test framework
as significantly more memory is available when running the u-boot
sandbox and so memory exhaustion does not occur. If there are other side
effects than memory leaking here then I'm not familiar enough with the
u-boot device model to spot them.

Each call to bind_drivers_pass() calls device_bind_by_name() for every
driver_info record, with no way of checking whether a particular
driver_info record has already been handled by a previous call to
bind_drivers_pass(). This leaks memory as each call to
device_bind_by_name() allocates a new device object.

> 
> Also, I think bind_drivers_pass() needs a function comment that
> describes in detail what is going on.

Agreed, I'll see what I can add when I revisit this.

> 
> Would it be possible to achieve the same effect while keeping that
> function the same as now?  See below.
> 
>>
>> diff --git a/drivers/core/lists.c b/drivers/core/lists.c
>> index c49695b24f00..82d479564121 100644
>> --- a/drivers/core/lists.c
>> +++ b/drivers/core/lists.c
>> @@ -51,13 +51,13 @@ struct uclass_driver *lists_uclass_lookup(enum uclass_id id)
>>         return NULL;
>>  }
>>
>> -static int bind_drivers_pass(struct udevice *parent, bool pre_reloc_only)
>> +static bool bind_drivers_pass(struct udevice *parent, bool pre_reloc_only,
>> +                             int *result)
>>  {
>>         struct driver_info *info =
>>                 ll_entry_start(struct driver_info, driver_info);
>>         const int n_ents = ll_entry_count(struct driver_info, driver_info);
>>         bool missing_parent = false;
>> -       int result = 0;
>>         int idx;
>>
>>         /*
>> @@ -98,12 +98,12 @@ static int bind_drivers_pass(struct udevice *parent, bool pre_reloc_only)
>>                                 drt->dev = dev;
>>                 } else if (ret != -EPERM) {
>>                         dm_warn("No match for driver '%s'\n", entry->name);
>> -                       if (!result || ret != -ENOENT)
>> -                               result = ret;
>> +                       if (!*result || ret != -ENOENT)
>> +                               *result = ret;
>>                 }
>>         }
>>
>> -       return result ? result : missing_parent ? -EAGAIN : 0;
>> +       return missing_parent;
>>  }
>>
>>  int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only)
>> @@ -117,13 +117,8 @@ int lists_bind_drivers(struct udevice *parent, bool pre_reloc_only)
>>          * always succeed on the first pass.
>>          */
>>         for (pass = 0; pass < 10; pass++) {
>> -               int ret;
>> -
>> -               ret = bind_drivers_pass(parent, pre_reloc_only);
>> -               if (!ret)
>> +               if (!bind_drivers_pass(parent, pre_reloc_only, &result))
>>                         break;
>> -               if (ret != -EAGAIN && !result)
>> -                       result = ret;
> 
> If there were something like this, could we drop the other changes?
> 
> if (ret) {
>    if (ret == -EAGAIN)
>       continue;
>    else if (!result)
> 
>       result = ret;
> }

I don't think this works exactly, as we want to break when (ret ==
-EAGAIN) instead of continue. However I have a variant which I think is
working which I can send as a v2.

> 
>>         }
>>
>>         return result;
>> --
>> 2.25.1
>>

-- 
Paul Barker
Principal Software Engineer
SanCloud Ltd

e: paul.barker at sancloud.com
w: https://sancloud.com/
-------------- next part --------------
A non-text attachment was scrubbed...
Name: OpenPGP_0xA67255DFCCE62ECD.asc
Type: application/pgp-keys
Size: 6758 bytes
Desc: OpenPGP public key
URL: <https://lists.denx.de/pipermail/u-boot/attachments/20221025/a848dbdf/attachment.key>
-------------- next part --------------
A non-text attachment was scrubbed...
Name: OpenPGP_signature
Type: application/pgp-signature
Size: 236 bytes
Desc: OpenPGP digital signature
URL: <https://lists.denx.de/pipermail/u-boot/attachments/20221025/a848dbdf/attachment.sig>


More information about the U-Boot mailing list