From 2de9d8e0d2fe3a1eb632def2245529067cb35db5 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Wed, 15 Sep 2021 10:09:37 -0700 Subject: driver core: fw_devlink: Improve handling of cyclic dependencies When we have a dependency of the form: Device-A -> Device-C Device-B Device-C -> Device-B Where, * Indentation denotes "child of" parent in previous line. * X -> Y denotes X is consumer of Y based on firmware (Eg: DT). We have cyclic dependency: device-A -> device-C -> device-B -> device-A fw_devlink current treats device-C -> device-B dependency as an invalid dependency and doesn't enforce it but leaves the rest of the dependencies as is. While the current behavior is necessary, it is not sufficient if the false dependency in this example is actually device-A -> device-C. When this is the case, device-C will correctly probe defer waiting for device-B to be added, but device-A will be incorrectly probe deferred by fw_devlink waiting on device-C to probe successfully. Due to this, none of the devices in the cycle will end up probing. To fix this, we need to go relax all the dependencies in the cycle like we already do in the other instances where fw_devlink detects cycles. A real world example of this was reported[1] and analyzed[2]. [1] - https://lore.kernel.org/lkml/0a2c4106-7f48-2bb5-048e-8c001a7c3fda@samsung.com/ [2] - https://lore.kernel.org/lkml/CAGETcx8peaew90SWiux=TyvuGgvTQOmO4BFALz7aj0Za5QdNFQ@mail.gmail.com/ Fixes: f9aa460672c9 ("driver core: Refactor fw_devlink feature") Cc: stable Reported-by: Marek Szyprowski Tested-by: Marek Szyprowski Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20210915170940.617415-2-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index e65dd803a453..316df6027093 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1772,14 +1772,21 @@ static int fw_devlink_create_devlink(struct device *con, * be broken by applying logic. Check for these types of cycles and * break them so that devices in the cycle probe properly. * - * If the supplier's parent is dependent on the consumer, then - * the consumer-supplier dependency is a false dependency. So, - * treat it as an invalid link. + * If the supplier's parent is dependent on the consumer, then the + * consumer and supplier have a cyclic dependency. Since fw_devlink + * can't tell which of the inferred dependencies are incorrect, don't + * enforce probe ordering between any of the devices in this cyclic + * dependency. Do this by relaxing all the fw_devlink device links in + * this cycle and by treating the fwnode link between the consumer and + * the supplier as an invalid dependency. */ sup_dev = fwnode_get_next_parent_dev(sup_handle); if (sup_dev && device_is_dependent(con, sup_dev)) { - dev_dbg(con, "Not linking to %pfwP - False link\n", - sup_handle); + dev_info(con, "Fixing up cyclic dependency with %pfwP (%s)\n", + sup_handle, dev_name(sup_dev)); + device_links_write_lock(); + fw_devlink_relax_cycle(con, sup_dev); + device_links_write_unlock(); ret = -EINVAL; } else { /* -- cgit From 5501765a02a6c324f78581e6bb8209d054fe13ae Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Wed, 15 Sep 2021 10:09:38 -0700 Subject: driver core: fw_devlink: Add support for FWNODE_FLAG_NEEDS_CHILD_BOUND_ON_ADD If a parent device is also a supplier to a child device, fw_devlink=on by design delays the probe() of the child device until the probe() of the parent finishes successfully. However, some drivers of such parent devices (where parent is also a supplier) expect the child device to finish probing successfully as soon as they are added using device_add() and before the probe() of the parent device has completed successfully. One example of such a case is discussed in the link mentioned below. Add a flag to make fw_devlink=on not enforce these supplier-consumer relationships, so these drivers can continue working. Link: https://lore.kernel.org/netdev/CAGETcx_uj0V4DChME-gy5HGKTYnxLBX=TH2rag29f_p=UcG+Tg@mail.gmail.com/ Fixes: ea718c699055 ("Revert "Revert "driver core: Set fw_devlink=on by default""") Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20210915170940.617415-3-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index 316df6027093..21d4cb5d3767 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -1722,6 +1722,25 @@ static int fw_devlink_create_devlink(struct device *con, struct device *sup_dev; int ret = 0; + /* + * In some cases, a device P might also be a supplier to its child node + * C. However, this would defer the probe of C until the probe of P + * completes successfully. This is perfectly fine in the device driver + * model. device_add() doesn't guarantee probe completion of the device + * by the time it returns. + * + * However, there are a few drivers that assume C will finish probing + * as soon as it's added and before P finishes probing. So, we provide + * a flag to let fw_devlink know not to delay the probe of C until the + * probe of P completes successfully. + * + * When such a flag is set, we can't create device links where P is the + * supplier of C as that would delay the probe of C. + */ + if (sup_handle->flags & FWNODE_FLAG_NEEDS_CHILD_BOUND_ON_ADD && + fwnode_is_ancestor_of(sup_handle, con->fwnode)) + return -EINVAL; + sup_dev = get_dev_from_fwnode(sup_handle); if (sup_dev) { /* -- cgit From 68223eeec70898cd5e42451a9168cd9b2808e248 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Wed, 15 Sep 2021 10:28:05 -0700 Subject: driver core: Set deferred probe reason when deferred by driver core When the driver core defers the probe of a device, set the deferred probe reason so that it's easier to debug. The deferred probe reason is available in debugfs under devices_deferred. Reviewed-by: Geert Uytterhoeven Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20210915172808.620546-2-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index 21d4cb5d3767..bf008040b101 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -975,6 +975,7 @@ int device_links_check_suppliers(struct device *dev) { struct device_link *link; int ret = 0; + struct fwnode_handle *sup_fw; /* * Device waiting for supplier to become available is not allowed to @@ -983,10 +984,11 @@ int device_links_check_suppliers(struct device *dev) mutex_lock(&fwnode_link_lock); if (dev->fwnode && !list_empty(&dev->fwnode->suppliers) && !fw_devlink_is_permissive()) { - dev_dbg(dev, "probe deferral - wait for supplier %pfwP\n", - list_first_entry(&dev->fwnode->suppliers, - struct fwnode_link, - c_hook)->supplier); + sup_fw = list_first_entry(&dev->fwnode->suppliers, + struct fwnode_link, + c_hook)->supplier; + dev_err_probe(dev, -EPROBE_DEFER, "wait for supplier %pfwP\n", + sup_fw); mutex_unlock(&fwnode_link_lock); return -EPROBE_DEFER; } @@ -1001,8 +1003,9 @@ int device_links_check_suppliers(struct device *dev) if (link->status != DL_STATE_AVAILABLE && !(link->flags & DL_FLAG_SYNC_STATE_ONLY)) { device_links_missing_supplier(dev); - dev_dbg(dev, "probe deferral - supplier %s not ready\n", - dev_name(link->supplier)); + dev_err_probe(dev, -EPROBE_DEFER, + "supplier %s not ready\n", + dev_name(link->supplier)); ret = -EPROBE_DEFER; break; } -- cgit From 76f130810b477243ce1312bf5754dc41ce7f91a8 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Wed, 15 Sep 2021 10:28:06 -0700 Subject: driver core: Create __fwnode_link_del() helper function The same code is repeated in multiple locations. Create a helper function for it. Reviewed-by: Geert Uytterhoeven Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20210915172808.620546-3-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 35 +++++++++++++++++++---------------- 1 file changed, 19 insertions(+), 16 deletions(-) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index bf008040b101..e215220545f8 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -101,6 +101,19 @@ out: return ret; } +/** + * __fwnode_link_del - Delete a link between two fwnode_handles. + * @link: the fwnode_link to be deleted + * + * The fwnode_link_lock needs to be held when this function is called. + */ +static void __fwnode_link_del(struct fwnode_link *link) +{ + list_del(&link->s_hook); + list_del(&link->c_hook); + kfree(link); +} + /** * fwnode_links_purge_suppliers - Delete all supplier links of fwnode_handle. * @fwnode: fwnode whose supplier links need to be deleted @@ -112,11 +125,8 @@ static void fwnode_links_purge_suppliers(struct fwnode_handle *fwnode) struct fwnode_link *link, *tmp; mutex_lock(&fwnode_link_lock); - list_for_each_entry_safe(link, tmp, &fwnode->suppliers, c_hook) { - list_del(&link->s_hook); - list_del(&link->c_hook); - kfree(link); - } + list_for_each_entry_safe(link, tmp, &fwnode->suppliers, c_hook) + __fwnode_link_del(link); mutex_unlock(&fwnode_link_lock); } @@ -131,11 +141,8 @@ static void fwnode_links_purge_consumers(struct fwnode_handle *fwnode) struct fwnode_link *link, *tmp; mutex_lock(&fwnode_link_lock); - list_for_each_entry_safe(link, tmp, &fwnode->consumers, s_hook) { - list_del(&link->s_hook); - list_del(&link->c_hook); - kfree(link); - } + list_for_each_entry_safe(link, tmp, &fwnode->consumers, s_hook) + __fwnode_link_del(link); mutex_unlock(&fwnode_link_lock); } @@ -1887,9 +1894,7 @@ static void __fw_devlink_link_to_consumers(struct device *dev) if (!own_link || ret == -EAGAIN) continue; - list_del(&link->s_hook); - list_del(&link->c_hook); - kfree(link); + __fwnode_link_del(link); } } @@ -1941,9 +1946,7 @@ static void __fw_devlink_link_to_suppliers(struct device *dev, if (!own_link || ret == -EAGAIN) continue; - list_del(&link->s_hook); - list_del(&link->c_hook); - kfree(link); + __fwnode_link_del(link); /* If no device link was created, nothing more to do. */ if (ret) -- cgit From ebd6823af378c7d91f80cbe83ce07b5f166744e4 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Wed, 15 Sep 2021 10:28:07 -0700 Subject: driver core: Add debug logs when fwnode links are added/deleted This will help with debugging fw_devlink issues. Reviewed-by: Geert Uytterhoeven Signed-off-by: Saravana Kannan Link: https://lore.kernel.org/r/20210915172808.620546-4-saravanak@google.com Signed-off-by: Greg Kroah-Hartman --- drivers/base/core.c | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'drivers/base/core.c') diff --git a/drivers/base/core.c b/drivers/base/core.c index e215220545f8..15986cc2fe5e 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -95,6 +95,8 @@ int fwnode_link_add(struct fwnode_handle *con, struct fwnode_handle *sup) list_add(&link->s_hook, &sup->consumers); list_add(&link->c_hook, &con->suppliers); + pr_debug("%pfwP Linked as a fwnode consumer to %pfwP\n", + con, sup); out: mutex_unlock(&fwnode_link_lock); @@ -109,6 +111,8 @@ out: */ static void __fwnode_link_del(struct fwnode_link *link) { + pr_debug("%pfwP Dropping the fwnode link to %pfwP\n", + link->consumer, link->supplier); list_del(&link->s_hook); list_del(&link->c_hook); kfree(link); -- cgit