On 8/19/19 5:00 PM, Saravana Kannan wrote: > On Sun, Aug 18, 2019 at 8:38 PM Frank Rowand <frowand.list@xxxxxxxxx> wrote: >> >> On 8/15/19 6:50 PM, Saravana Kannan wrote: >>> On Wed, Aug 7, 2019 at 7:04 PM Frank Rowand <frowand.list@xxxxxxxxx> wrote: >>>> >>>>> Date: Tue, 23 Jul 2019 17:10:54 -0700 >>>>> Subject: [PATCH v7 1/7] driver core: Add support for linking devices during >>>>> device addition >>>>> From: Saravana Kannan <saravanak@xxxxxxxxxx> >>>>> >>>>> When devices are added, the bus might want to create device links to track >>>>> functional dependencies between supplier and consumer devices. This >>>>> tracking of supplier-consumer relationship allows optimizing device probe >>>>> order and tracking whether all consumers of a supplier are active. The >>>>> add_links bus callback is added to support this. >>>> >>>> Change above to: >>>> >>>> When devices are added, the bus may create device links to track which >>>> suppliers a consumer device depends upon. This >>>> tracking of supplier-consumer relationship may be used to defer probing >>>> the driver of a consumer device before the driver(s) for its supplier device(s) >>>> are probed. It may also be used by a supplier driver to determine if >>>> all of its consumers have been successfully probed. >>>> The add_links bus callback is added to create the supplier device links >>>> >>>>> >>>>> However, when consumer devices are added, they might not have a supplier >>>>> device to link to despite needing mandatory resources/functionality from >>>>> one or more suppliers. A waiting_for_suppliers list is created to track >>>>> such consumers and retry linking them when new devices get added. >>>> >>>> Change above to: >>>> >>>> If a supplier device has not yet been created when the consumer device attempts >>>> to link it, the consumer device is added to the wait_for_suppliers list. >>>> When supplier devices are created, the supplier device link will be added to >>>> the relevant consumer devices on the wait_for_suppliers list. >>>> >>> >>> I'll take these commit text suggestions if we decide to revert the >>> entire series at the end of this review. >>> >>>>> >>>>> Signed-off-by: Saravana Kannan <saravanak@xxxxxxxxxx> >>>>> --- >>>>> drivers/base/core.c | 83 ++++++++++++++++++++++++++++++++++++++++++ >>>>> include/linux/device.h | 14 +++++++ >>>>> 2 files changed, 97 insertions(+) >>>>> >>>>> diff --git a/drivers/base/core.c b/drivers/base/core.c >>>>> index da84a73f2ba6..1b4eb221968f 100644 >>>>> --- a/drivers/base/core.c >>>>> +++ b/drivers/base/core.c >>>>> @@ -44,6 +44,8 @@ early_param("sysfs.deprecated", sysfs_deprecated_setup); >>>>> #endif >>>>> >>>>> /* Device links support. */ >>>>> +static LIST_HEAD(wait_for_suppliers); >>>>> +static DEFINE_MUTEX(wfs_lock); >>>>> >>>>> #ifdef CONFIG_SRCU >>>>> static DEFINE_MUTEX(device_links_lock); >>>>> @@ -401,6 +403,51 @@ struct device_link *device_link_add(struct device *consumer, >>>>> } >>>>> EXPORT_SYMBOL_GPL(device_link_add); >>>>> >>>>> +/** >>>> >>>>> + * device_link_wait_for_supplier - Mark device as waiting for supplier >>>> >>>> * device_link_wait_for_supplier - Add device to wait_for_suppliers list >>> >> >> As a meta-comment, I found this series very hard to understand in the context >> of reading the new code for the first time. When I read the code again in >> six months or a year or two years it will not be in near term memory and it >> will be as if I am reading it for the first time. A lot of my suggestions >> for changes of names are in that context -- the current names may be fine >> when one has recently read the code, but not so much when trying to read >> the whole thing again with a blank mind. > > Thanks for the context. > >> The code also inherits a good deal of complexity because it does not stand >> alone in a nice discrete chunk, but instead delicately weaves into a more >> complex body of code. > > I'll take this as a compliment :) Please do! > >> When I was trying to understand the code, I wrote a lot of additional >> comments within my reply email to provide myself context, information >> about various things, and questions that I needed to answer (or if I >> could not answer to then ask you). Then I ended up being able to remove >> many of those notes before sending the reply. >> >> >>> I intentionally chose "Mark device..." because that's a better >>> description of the semantics of the function instead of trying to >>> describe the implementation. Whether I'm using a linked list or some >>> other data structure should not be the one line documentation of a >>> function. Unless the function is explicitly about operating on that >>> specific data structure. >> >> I agree with the intent of trying to describe the semantics of a function, >> especially at the API level where other systems (or drivers) would be using >> the function. But for this case the function is at the implementation level >> and describing explicitly what it is doing makes this much more readable for >> me. > > Are you distinguishing between API level vs implementation level based > on the function being "static"/not exported? I believe the earlier No, being static helps say a function is not API, but an function that is not static may be intended to be used in a limited and constrained manner. I distinguished based on the usage of the function. > version of this series had this function as an exported API. So maybe > that's why I had it as "Mark device". > >> I also find "Mark device" to be vague and not descriptive of what the >> intent is. >> >>> >>>> >>>> >>>>> + * @consumer: Consumer device >>>>> + * >>>>> + * Marks the consumer device as waiting for suppliers to become available. The >>>>> + * consumer device will never be probed until it's unmarked as waiting for >>>>> + * suppliers. The caller is responsible for adding the link to the supplier >>>>> + * once the supplier device is present. >>>>> + * >>>>> + * This function is NOT meant to be called from the probe function of the >>>>> + * consumer but rather from code that creates/adds the consumer device. >>>>> + */ >>>>> +static void device_link_wait_for_supplier(struct device *consumer) >>>>> +{ >>>>> + mutex_lock(&wfs_lock); >>>>> + list_add_tail(&consumer->links.needs_suppliers, &wait_for_suppliers); >>>>> + mutex_unlock(&wfs_lock); >>>>> +} >>>>> + >>>>> +/** >>>> >>>> >>>>> + * device_link_check_waiting_consumers - Try to remove from supplier wait list >>>>> + * >>>>> + * Loops through all consumers waiting on suppliers and tries to add all their >>>>> + * supplier links. If that succeeds, the consumer device is unmarked as waiting >>>>> + * for suppliers. Otherwise, they are left marked as waiting on suppliers, >>>>> + * >>>>> + * The add_links bus callback is expected to return 0 if it has found and added >>>>> + * all the supplier links for the consumer device. It should return an error if >>>>> + * it isn't able to do so. >>>>> + * >>>>> + * The caller of device_link_wait_for_supplier() is expected to call this once >>>>> + * it's aware of potential suppliers becoming available. >>>> >>>> Change above comment to: >>>> >>>> * device_link_add_supplier_links - add links from consumer devices to >>>> * supplier devices, leaving any consumer >>>> * with inactive suppliers on the >>>> * wait_for_suppliers list >>> >>> I didn't know that the first one line comment could span multiple >>> lines. Good to know. >>> >>> >>>> * Scan all consumer devices in the devicetree. >>> >>> This function doesn't have anything to do with devicetree. I've >>> intentionally kept all OF related parts out of the driver/core because >>> I hope that other busses can start using this feature too. So I can't >>> take this bit. >> >> My comment is left over from when I was taking notes, trying to understand the >> code. >> >> At the moment, only devicetree is used as a source of the dependency information. >> The comment would better be re-phrased as: >> >> * Scan all consumer devices in the firmware description of the hardware topology >> > > Ok > >> I did not ask why this feature is tied to _only_ the platform bus, but will now. > > Because devicetree and platform bus the only ones I'm familiar with. > If other busses want to add this, I'd be happy to help with code > and/or direction/review. But I won't pretend to know anything about > ACPI. Sorry, you don't get to ignore other buses because you are not familiar with them. I am not aware of any reason to exclude devices that on other buses and your answer below does not provide a valid technical reason why the new feature is correct when it excludes all other buses. > >> I do not know of any reason that a consumer / supplier relationship can not be >> between devices on different bus types. Do you know of such a reason? > > Yes, it's hypothetically possible. But I haven't seen such a > relationship being defined in DT. Nor somewhere else where this might > be captured. So, how common/realistic is it? It is entirely legal. I have no idea how common it is but that is not a valid reason to exclude other buses from the feature. > >>> >>>> For any supplier device that >>>> * is not already linked to the consumer device, add the supplier to the >>>> * consumer device's device links. >>>> * >>>> * If all of a consumer device's suppliers are available then the consumer >>>> * is removed from the wait_for_suppliers list (if previously on the list). >>>> * Otherwise the consumer is added to the wait_for_suppliers list (if not >>>> * already on the list). >>> >>> Honestly, I don't think this is any better than what I already have. >> >> Note that my version of these comments was written while I was reading the code, >> and did not have any big picture understanding yet. This will likely also be >> the mind set of most everyone who reads this code in the future, once it is >> woven into the kernel. >> >> If you don't like the change, I can revisit it in a later version of the >> patch set. > > I'll take in all the ones I feel are reasonable or don't feel strongly > about. We can revisit the rest later. > >>> >>>> * The add_links bus callback must return 0 if it has found and added all >>>> * the supplier links for the consumer device. It must return an error if >>>> * it is not able to do so. >>>> * >>>> * The caller of device_link_wait_for_supplier() is expected to call this once >>>> * it is aware of potential suppliers becoming available. >>>> >>>> >>>> >>>>> + */ >>>>> +static void device_link_check_waiting_consumers(void) >>>> >>>> Function name is misleading and hides side effects. >>>> >>>> I have not come up with a name that does not hide side effects, but a better >>>> name would be: >>>> >>>> device_link_add_supplier_links() >>> >>> I kinda agree that it could afford a better name. The current name is >>> too similar to device_links_check_suppliers() and I never liked that. >> >> Naming new fields or variables related to device links looks pretty >> challenging to me, because of the desire to be part of device links >> and not a wart pasted on the side. So I share the pain in trying >> to find good names. >> >>> >>> Maybe device_link_add_missing_suppliers()? >> >> My first reaction was "yes, that sounds good". But then I stopped and >> tried to read the name out of context. The name is not adding the >> missing suppliers, it is saving the information that a supplier is >> not yet available (eg, is "missing"). I struggled in coming up with Reading what you say below, and looking at the code again, what I say in that sentence is backwards. It is not adding the missing supplier device links, it is instead adding existing supplier device inks. >> the name that I suggested. We can keep thinking. > > No, this function _IS_ about adding links to suppliers. These You are mis-reading what I wrote. I said the function "is not adding the missing suppliers". You are converting that to "is not adding links to the missing suppliers". My suggested name was hinting "add_supplier_links", which is what you say it does below. The name you suggest is hinting "add_missing_suppliers". Do you see the difference? > consumers were "saved" as "not yet having the supplier" earlier by > device_link_wait_for_supplier(). This function doesn't do that. This > function is just trying to see if those missing suppliers are present > now and if so adding a link to them from the "saved" consumers. I > think device_link_add_missing_suppliers() is actually a pretty good > name. Let me know what you think now. > >> >> >>> >>> I don't think we need "links" repeated twice in the function name. >> >> Yeah, I didn't like that either. >> >> >>> With this suggestion, what side effect is hidden in your opinion? That >>> the fully linked consumer is removed from the "waiting for suppliers" >>> list? >> >> The side effect is that the function does not merely do a check. It also >> adds missing suppliers to a list. > > No, it doesn't do that. I can't keep a list of things that aren't > allocated yet :). In the whole patch series, we only keep a list of things > (consumers) that are waiting on other things (missing suppliers). OK, as I noted above, I stated that backwards. It is adding links for existing suppliers, not for the missing suppliers. > >>> >>> Maybe device_link_try_removing_from_wfs()? >> >> I like that, other than the fact that it still does not provide a clue >> that the function is potentially adding suppliers to a list. > > It doesn't. How would you add a supplier device to a list if the > device itself isn't there? :) Again, that should be existing suppliers, as you noted. But the point stands that the function is potentially adding links. > >> I think >> part of the challenge is that the function does two things: (1) a check, >> and (2) potentially adding missing suppliers to a list. Maybe a simple >> one line comment at the call site, something like: >> >> /* adds missing suppliers to wfs */ >> >> >>> >>> I'll wait for us to agree on a better name here before I change this. >>> >>>>> +{ >>>>> + struct device *dev, *tmp; >>>>> + >>>>> + mutex_lock(&wfs_lock); >>>>> + list_for_each_entry_safe(dev, tmp, &wait_for_suppliers, >>>>> + links.needs_suppliers) >>>>> + if (!dev->bus->add_links(dev)) >>>>> + list_del_init(&dev->links.needs_suppliers); >>>> >>>> Empties dev->links.needs_suppliers, but does not remove dev from >>>> wait_for_suppliers list. Where does that happen? >>> >>> I'll chalk this up to you having a long day or forgetting your coffee >>> :) list_del_init() does both of those things because needs_suppliers >>> is the node and wait_for_suppliers is the list. >> >> Yes, brain mis-fire on my part. I'll have to go back and look at the >> list related code again. >> >> >>> >>>> >>>>> + mutex_unlock(&wfs_lock); >>>>> +} >>>>> + >>>>> static void device_link_free(struct device_link *link) >>>>> { >>>>> while (refcount_dec_not_one(&link->rpm_active)) >>>>> @@ -535,6 +582,19 @@ int device_links_check_suppliers(struct device *dev) >>>>> struct device_link *link; >>>>> int ret = 0; >>>>> >>>>> + /* >>>>> + * If a device is waiting for one or more suppliers (in >>>>> + * wait_for_suppliers list), it is not ready to probe yet. So just >>>>> + * return -EPROBE_DEFER without having to check the links with existing >>>>> + * suppliers. >>>>> + */ >>>> >>>> Change comment to: >>>> >>>> /* >>>> * Device waiting for supplier to become available is not allowed >>>> * to probe >>>> */ >>> >>> Po-tay-to. Po-tah-to? I think my comment is just as good. >> >> If just as good and shorter, then better. >> >> Also the original says "it is not ready to probe". That is not correct. It >> is ready to probe, it is just that the probe attempt will return -EPROBE_DEFER. >> Nit picky on my part, but tiny things like that mean I have to think harder. >> I have to think "why is it not ready to probe?". Maybe my version should have >> instead been something like: >> >> * Device waiting for supplier to become available will return >> * -EPROBE_DEFER if probed. Avoid the unneeded processing. >> >>> >>>>> + mutex_lock(&wfs_lock); >>>>> + if (!list_empty(&dev->links.needs_suppliers)) { >>>>> + mutex_unlock(&wfs_lock); >>>>> + return -EPROBE_DEFER; >>>>> + } >>>>> + mutex_unlock(&wfs_lock); >>>>> + >>>>> device_links_write_lock(); >>>> >>>> Update Documentation/driver-api/device_link.rst to reflect the >>>> check of &dev->links.needs_suppliers in device_links_check_suppliers(). >>> >>> Thanks! Will do. >>> >>>> >>>>> >>>>> list_for_each_entry(link, &dev->links.suppliers, c_node) { >>>>> @@ -812,6 +872,10 @@ static void device_links_purge(struct device *dev) >>>>> { >>>>> struct device_link *link, *ln; >>>>> >>>>> + mutex_lock(&wfs_lock); >>>>> + list_del(&dev->links.needs_suppliers); >>>>> + mutex_unlock(&wfs_lock); >>>>> + >>>>> /* >>>>> * Delete all of the remaining links from this device to any other >>>>> * devices (either consumers or suppliers). >>>>> @@ -1673,6 +1737,7 @@ void device_initialize(struct device *dev) >>>>> #endif >>>>> INIT_LIST_HEAD(&dev->links.consumers); >>>>> INIT_LIST_HEAD(&dev->links.suppliers); >>>>> + INIT_LIST_HEAD(&dev->links.needs_suppliers); >>>>> dev->links.status = DL_DEV_NO_DRIVER; >>>>> } >>>>> EXPORT_SYMBOL_GPL(device_initialize); >>>>> @@ -2108,6 +2173,24 @@ int device_add(struct device *dev) >>>>> BUS_NOTIFY_ADD_DEVICE, dev); >>>>> >>>>> kobject_uevent(&dev->kobj, KOBJ_ADD); >>>> >>>>> + >>>>> + /* >>>>> + * Check if any of the other devices (consumers) have been waiting for >>>>> + * this device (supplier) to be added so that they can create a device >>>>> + * link to it. >>>>> + * >>>>> + * This needs to happen after device_pm_add() because device_link_add() >>>>> + * requires the supplier be registered before it's called. >>>>> + * >>>>> + * But this also needs to happe before bus_probe_device() to make sure >>>>> + * waiting consumers can link to it before the driver is bound to the >>>>> + * device and the driver sync_state callback is called for this device. >>>>> + */ >>>> >>>> /* >>>> * Add links to dev from any dependent consumer that has dev on it's >>>> * list of needed suppliers >>> >>> There is no list of needed suppliers. >> >> "the other devices (consumers) have been waiting for this device (supplier)". >> Isn't that a list of needed suppliers? > > No, that's a list of consumers that needs_suppliers. > >>> >>>> (links.needs_suppliers). Device_pm_add() >>>> * must have previously registered dev to allow the links to be added. >>>> * >>>> * The consumer links must be created before dev is probed because the >>>> * sync_state callback for dev will use the consumer links. >>>> */ >>> >>> I think what I wrote is just as clear. >> >> The original comment is vague. It does not explain why consumer links must be >> created before the probe. I had to go off and read other code to determine >> why that is true. >> >> And again, brevity is better if otherwise just as clear. >> >> >>> >>>> >>>>> + device_link_check_waiting_consumers(); >>>>> + >>>>> + if (dev->bus && dev->bus->add_links && dev->bus->add_links(dev)) >>>>> + device_link_wait_for_supplier(dev); >>>>> + >>>>> bus_probe_device(dev); >>>>> if (parent) >>>>> klist_add_tail(&dev->p->knode_parent, >>>>> diff --git a/include/linux/device.h b/include/linux/device.h >>>>> index c330b75c6c57..5d70babb7462 100644 >>>>> --- a/include/linux/device.h >>>>> +++ b/include/linux/device.h >>>>> @@ -78,6 +78,17 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *); >>>>> * -EPROBE_DEFER it will queue the device for deferred probing. >>>>> * @uevent: Called when a device is added, removed, or a few other things >>>>> * that generate uevents to add the environment variables. >>>> >>>>> + * @add_links: Called, perhaps multiple times per device, after a device is >>>>> + * added to this bus. The function is expected to create device >>>>> + * links to all the suppliers of the input device that are >>>>> + * available at the time this function is called. As in, the >>>>> + * function should NOT stop at the first failed device link if >>>>> + * other unlinked supplier devices are present in the system. >>>> >>>> * @add_links: Called after a device is added to this bus. >>> >>> Why are you removing the "perhaps multiple times" part? that's true >>> and that's how some of the other ops are documented. >> >> I didn't remove it. I rephrased it with a little bit more explanation as >> "If some suppliers are not yet available, this function will be >> called again when the suppliers become available." (below). >> >> >>> >>>> The function is >>>> * expected to create device links to all the suppliers of the >>>> * device that are available at the time this function is called. >>>> * The function must NOT stop at the first failed device link if >>>> * other unlinked supplier devices are present in the system. >>>> * If some suppliers are not yet available, this function will be >>>> * called again when the suppliers become available. >>>> >>>> but add_links() not needed, so moving this comment to of_link_to_suppliers() >>> >>> Sorry, I'm not sure I understand. Can you please explain what you are >>> trying to say? of_link_to_suppliers() is just one implementation of >>> add_links(). The comment above is try for any bus trying to implement >>> add_links(). >> >> This is conflating bus with the source of the firmware description of the >> hardware topology. For drivers that use various APIs to access firmware >> description of topology that may be either devicetree or ACPI the access >> is done via fwnode_operations, based on struct device.fwnode (if I recall >> properly). >> >> I failed to completely address why add_links() is not needed. The answer >> is that there should be a single function called for all buses. Then >> the proper firmware data source would be accessed via a struct fwnode_operations. >> >> I think I left this out because I had not yet asked why this feature is >> tied only to the platform bus. Which I asked earlier in this reply. > > Thanks for the pointer about fwnode and fwnode_operations. I wasn't > aware of those. I see where you are going with this. I see a couple of > problems with this approach though: > > 1. How you interpret the properties of a fwnode is specific to the fw > type. The clocks DT property isn't going to have the same definition > in ACPI or some other firmware. Heck, I don't know if ACPI even has a > clocks like property. So have one function to parse all the FW types > doesn't make a lot of sense. The functions in fwnode_operations are specific to the proper firmware. So there is a set of functions in a struct fwnode_operations for devicetree that only know about devicetree. And there is a different variable of type fwnode_operations that is initialized with ACPI specific functions. > > 2. If this common code is implemented as part of driver/base/, then at > a minimum, I'll have to check if a fwnode is a DT node before I start > interpreting the properties of a device's fwnode. But that means I'll > have to include linux/of.h to use is_of_node(). I don't like having > driver/base code depend on OF or platform or ACPI headers. You just use the function in the device's fwnode_operations (I think, I would have to go look at the precise way the code works because it has been quite a while since I've looked at it). > > 3. The supplier info doesn't always need to come from a firmware. So I > don't want to limit it to that? If you can find another source of topology info, then I would expect that another set of fwnode_operations functions would be created for the info source. > > Also, I don't necessarily see this as conflating firmware (DT, ACPI, > etc) with the bus (platform bus, ACPI bus, PCI bus). Whoever creates > the device seems like the entity best suited to figure out the > suppliers of the device (apart from the driver, obviously). So the bus > deciding the suppliers doesn't seem wrong to me. Patch 3 assigns the devicetree add_links function to the platform bus. It seems incorrect to me for of_platform_default_populate_init() to be changing a field in platform_bus_type. of_platform_default_populate_init() ... platform_bus_type.add_links = of_link_to_suppliers; > > In this specific case, I'm trying to address DT for now and leaving > ACPI to whoever else wants to add device links based on ACPI data. > Most OF/DT based devices end up in platform bus. So I'm just handling > this in platform bus. If some other person wants this to work for ACPI > bus or PCI bus, they are welcome to implement add_links() for those > busses? I'm nowhere close to an expert on those. Devicetree is not limited to the platform bus. > >>> >>>> >>>> >>>>> + * >>>>> + * Return 0 if device links have been successfully created to all >>>>> + * the suppliers of this device. Return an error if some of the >>>>> + * suppliers are not yet available and this function needs to be >>>>> + * reattempted in the future. >>>> >>>> * >>>> * Return 0 if device links have been successfully created to all >>>> * the suppliers of this device. Return an error if some of the >>>> * suppliers are not yet available. >>>> >>>> >>>>> * @probe: Called when a new device or driver add to this bus, and callback >>>>> * the specific driver's probe to initial the matched device. >>>>> * @remove: Called when a device removed from this bus. >>>>> @@ -122,6 +133,7 @@ struct bus_type { >>>>> >>>>> int (*match)(struct device *dev, struct device_driver *drv); >>>>> int (*uevent)(struct device *dev, struct kobj_uevent_env *env); >>>> >>>> >>>>> + int (*add_links)(struct device *dev); >>>> >>>> ^^^^^^^^^ add_supplier ??? >>>> ^^^^^^^^^ add_suppliers ??? >>>> >>>> ^^^^^^^^^ link_suppliers ??? >>>> >>>> ^^^^^^^^^ add_supplier_dependency ??? >>>> ^^^^^^^^^ add_supplier_dependencies ??? >>>> add_links() not needed >>> >>> add_links() was an intentional decision. There's no requirement that >>> the bus should only create links from this device to its suppliers. If >>> the bus also knows the consumers of this device (dev), then it >>> can/should add those too. >> >> Is creating links to consumers of this device implemented in this patch >> series? If so, I overlooked it and will have to consider how that >> fits in to the design. >> >> >>> So, it shouldn't have "suppliers" in the >>> name. >>> >>>>> int (*probe)(struct device *dev); >>>>> int (*remove)(struct device *dev); >>>>> void (*shutdown)(struct device *dev); >>>> >>>> >>>> >>>> >>>>> @@ -893,11 +905,13 @@ enum dl_dev_state { >>>>> * struct dev_links_info - Device data related to device links. >>>>> * @suppliers: List of links to supplier devices. >>>>> * @consumers: List of links to consumer devices. >>>> >>>>> + * @needs_suppliers: Hook to global list of devices waiting for suppliers. >>>> >>>> * @needs_suppliers: List of devices deferring probe until supplier drivers >>>> * are successfully probed. >>> >>> It's "need suppliers". As in, this is a device that "needs suppliers". >>> So, no, this is not a list. This is a node in a list. And all "nodes >>> in a list" are documented as "Hook" in rest of places in this file. So >>> I think the documentation is correct as is. >> >> Aha, I got confused about that while trying to keep everything straight. >> >> Somehow I managed to conflate needs_suppliers with the links between >> consumers and suppliers that are create via device_link_add(). >> >> So original comment is fine. >> >> It is getting late, so I'll continue with patches 2 and 3 tomorrow. >> > > Thanks for the review. > > -Saravana >