Re: [PATCH] driver core: Extend device_is_dependent()

From: Stephan Gerhold
Date: Fri Jan 15 2021 - 16:02:41 EST


On Fri, Jan 15, 2021 at 09:20:54AM -0800, Saravana Kannan wrote:
> On Fri, Jan 15, 2021 at 5:03 AM Rafael J. Wysocki <rafael@xxxxxxxxxx> wrote:
> > On Fri, Jan 15, 2021 at 11:03 AM Stephan Gerhold <stephan@xxxxxxxxxxx> wrote:
> > > On Thu, Jan 14, 2021 at 11:31:12AM -0800, Saravana Kannan wrote:
> > > > On Thu, Jan 14, 2021 at 10:41 AM Rafael J. Wysocki <rjw@xxxxxxxxxxxxx> wrote:
> > > > >
> > > > > From: Rafael J. Wysocki <rafael.j.wysocki@xxxxxxxxx>
> > > > >
> > > > > When adding a new device link, device_is_dependent() is used to
> > > > > check whether or not the prospective supplier device does not
> > > > > depend on the prospective consumer one to avoid adding loops
> > > > > to the graph of device dependencies.
> > > > >
> > > > > However, device_is_dependent() does not take the ancestors of
> > > > > the target device into account, so it may not detect an existing
> > > > > reverse dependency if, for example, the parent of the target
> > > > > device depends on the device passed as its first argument.
> > > > >
> > > > > For this reason, extend device_is_dependent() to also check if
> > > > > the device passed as its first argument is an ancestor of the
> > > > > target one and return 1 if that is the case.
> > > > >
> > > > > Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@xxxxxxxxx>
> > > > > Reported-by: Stephan Gerhold <stephan@xxxxxxxxxxx>
> > > > > ---
> > > > > drivers/base/core.c | 12 +++++++++++-
> > > > > 1 file changed, 11 insertions(+), 1 deletion(-)
> > > > >
> > > > > Index: linux-pm/drivers/base/core.c
> > > > > ===================================================================
> > > > > --- linux-pm.orig/drivers/base/core.c
> > > > > +++ linux-pm/drivers/base/core.c
> > > > > @@ -208,6 +208,16 @@ int device_links_read_lock_held(void)
> > > > > #endif
> > > > > #endif /* !CONFIG_SRCU */
> > > > >
> > > > > +static bool device_is_ancestor(struct device *dev, struct device *target)
> > > > > +{
> > > > > + while (target->parent) {
> > > > > + target = target->parent;
> > > > > + if (dev == target)
> > > > > + return true;
> > > > > + }
> > > > > + return false;
> > > > > +}
> > > > > +
> > > > > /**
> > > > > * device_is_dependent - Check if one device depends on another one
> > > > > * @dev: Device to check dependencies for.
> > > > > @@ -221,7 +231,7 @@ int device_is_dependent(struct device *d
> > > > > struct device_link *link;
> > > > > int ret;
> > > > >
> > > > > - if (dev == target)
> > > > > + if (dev == target || device_is_ancestor(dev, target))
> > > > > return 1;
> > > > >
> > > > > ret = device_for_each_child(dev, target, device_is_dependent);
> > > > >
> > > >
> > > > The code works, but it's not at all obvious what it's doing. Because,
> > > > at first glance, it's easy to mistakenly think that it's trying to
> > > > catch this case:
> > > > dev <- child1 <- child2 <- target
> > > >
> > >
> > > Isn't this pretty much the case we are trying to catch? I have:
> > >
> > > 78d9000.usb <- ci_hdrc.0 <- ci_hdrc.0.ulpi <- phy-ci_hdrc.0.ulpi.0
> > >
> > > then something attempts to create a device link with
> > > consumer = 78d9000.usb, supplier = phy-ci_hdrc.0.ulpi.0, and to check if
> > > that is allowed we call device_is_dependent() with dev = 78d9000.usb,
> > > target = phy-ci_hdrc.0.ulpi.0.
> > >
> > > Note that this case would normally be covered by the device_for_each_child().
> > > It's not in this case because the klist_children of 78d9000.usb
> > > is updated too late.
> >
> > Exactly.
>
> Stephan,
>
> What device/driver is this? Is this a dwc3 device/driver? That driver
> does some weird/incorrect stuff the last time I checked.
>

I described my situation in this mail thread:
https://lore.kernel.org/lkml/X%2FycQpu7NIGI969v@xxxxxxxxxxx/

It's USB, but chipidea on apq8016-sbc in this case. The situation is
definitely kind of weird, but not sure if it is wrong per-se.

Thanks,
Stephan