Received: by 2002:a25:ad19:0:0:0:0:0 with SMTP id y25csp8991206ybi; Tue, 23 Jul 2019 19:36:10 -0700 (PDT) X-Google-Smtp-Source: APXvYqy/6mGiQDK5KCXielaP0MmNXTrjVsO52W/VCikSHJTDX2rvkXG9QudL8lSRKxLbkMtXpuMm X-Received: by 2002:a62:78c2:: with SMTP id t185mr9106107pfc.142.1563935770058; Tue, 23 Jul 2019 19:36:10 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1563935770; cv=none; d=google.com; s=arc-20160816; b=A9xpdQ2YwXpDQYcrQjTuK/4+3olW0SzQU0ymXG0aWkmth/YZcYqti0a5HCp7j+eEa0 dIEy8R7wBLJQ6QgJrTSuTMB9LeR8msZ1qSgjM+GW21yGFTWSCAE2BDJfQEy8PR1RQGk3 mr66AViRTjjaG+mbSdh3usZkNlLGzcb+OPh79qv0MFTlM8h/OffUu2toucYPFodiHmHr QZv24IkNl2wieAN3VJobP+3sC+KzPnWnDDmoOO1VQHoj5uUcYxx7DbUjdMo9OfmSoHli eQS5zGziyOdNBNV7Cuw4HdFfNaxBZUodI9IR1sxYGah6mAy3bSzBlkc7hCq4Lkg5mH9U d8jw== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=list-id:precedence:sender:cc:to:from:subject:references :mime-version:message-id:in-reply-to:date:dkim-signature; bh=l3AiDzXuhUM+bCuy8gx3HoMz2dQhMIpUyx7lspv3OUk=; b=zcXhT/Lt+erNYWgZQHPJJyUd/2/yvYVef9yPV8PsWKDAN4WSSsWFlksjZ9vQ1x/UJo irwAv6YbA8fIJ4j+y1wj8Uh/gu+J+vPtTQATNuc5FktnZfXi9dljMpc9vJ67zfXHro5I sGnVzoqOReZMNv+8UxmZ2XWLm2f27q3fVUeKFruL2yUiWXZoChxEYu5WEw9FI4WBMH23 9zjdVf8XBkvHFzJ7Zg9CDreC85Lu9nPGVhz2H4Bcec6DsYrJA52lwLDq4v2rRJAzs15Q eSCYmtn8aKmVX7hJb3qhxF6tnwGzZBzmcjp1O69WyyKgVt8dRcTv9Y6mTit1FBxs1LKH KoFQ== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=fDbx4YoS; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Return-Path: Received: from vger.kernel.org (vger.kernel.org. [209.132.180.67]) by mx.google.com with ESMTP id r3si16234599pls.176.2019.07.23.19.35.55; Tue, 23 Jul 2019 19:36:10 -0700 (PDT) Received-SPF: pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) client-ip=209.132.180.67; Authentication-Results: mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b=fDbx4YoS; spf=pass (google.com: best guess record for domain of linux-kernel-owner@vger.kernel.org designates 209.132.180.67 as permitted sender) smtp.mailfrom=linux-kernel-owner@vger.kernel.org; dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=google.com Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727527AbfGXALL (ORCPT + 99 others); Tue, 23 Jul 2019 20:11:11 -0400 Received: from mail-vk1-f201.google.com ([209.85.221.201]:32812 "EHLO mail-vk1-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727507AbfGXALK (ORCPT ); Tue, 23 Jul 2019 20:11:10 -0400 Received: by mail-vk1-f201.google.com with SMTP id l80so19934724vkl.0 for ; Tue, 23 Jul 2019 17:11:08 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20161025; h=date:in-reply-to:message-id:mime-version:references:subject:from:to :cc; bh=l3AiDzXuhUM+bCuy8gx3HoMz2dQhMIpUyx7lspv3OUk=; b=fDbx4YoSncTaBDrPNUnrnTmCfZtrKhhhYrN5WHXO5Ixcf/rQ+sR2Gkqd3DpWxoqvQV sGqlwrqsSYgxiNnJWPXSwKxKg0uaXrn4lFO6ELhJtjeH+bs38vKgy7KVslDmaG7kzQ3D vtpe7lWa9hrmCnpDyMH6qq0RVXtkMVjMeWlU4gu6GtrslNCpo8x9qpVXTb+SkWPQflAd 39usHyzaihzj0579OwuPcEe7HQnJyq+eH+by+pVygVR/xjEgoduxcuZELtrcDPtxe7BI w30/CQEpPcMA+SmhhjQY04KzrWU9+sr4PLRpjIAz13lbNOKsuFThXaTplwSGQzF19tJ/ Ex+A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:date:in-reply-to:message-id:mime-version :references:subject:from:to:cc; bh=l3AiDzXuhUM+bCuy8gx3HoMz2dQhMIpUyx7lspv3OUk=; b=Lj+dFguRSUQPnXD8K4EdjWKBJdC58pN9Stmyee4ROLt9OVCjaBUfyiYC800b/pKMgQ 243p4H9f5P9RWdAZMAwhfKcp8KIB2IKhFy+uPx9fw2N5WtF7cW7LV2/KHmOfw5uiB2Rr bEUJAPAQp2jdiDjm+9kHbhy36qxr3RyKK07dOexse5s4Rc1yFMPaMo1nyjOClfx3EFfF mOVIJSUHcV6nT3Sty4MJrnLRc9r8ihf09Kod/aqFqe5s1R/9Jo8iVdv4RA8OPaA4yjUA iYuGs6BVrB5pmN7V0brScNMTWZb4NzXWtYpHDWOE0CrtguEm76dNBavs6XEhFbYtdUjh 2d3g== X-Gm-Message-State: APjAAAVfPHOjOUc6NMCrKUffnJ+DKgrO2G/zWCkqq9eXw9npsR+NvEmo UkNqhKDC1EJ/wd4YhcMGmqGwRsqks5JvNqo= X-Received: by 2002:a67:fb02:: with SMTP id d2mr40618831vsr.207.1563927068278; Tue, 23 Jul 2019 17:11:08 -0700 (PDT) Date: Tue, 23 Jul 2019 17:10:54 -0700 In-Reply-To: <20190724001100.133423-1-saravanak@google.com> Message-Id: <20190724001100.133423-2-saravanak@google.com> Mime-Version: 1.0 References: <20190724001100.133423-1-saravanak@google.com> X-Mailer: git-send-email 2.22.0.709.g102302147b-goog Subject: [PATCH v7 1/7] driver core: Add support for linking devices during device addition From: Saravana Kannan To: Rob Herring , Mark Rutland , Greg Kroah-Hartman , "Rafael J. Wysocki" , Frank Rowand Cc: Saravana Kannan , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, David Collins , kernel-team@android.com Content-Type: text/plain; charset="UTF-8" Sender: linux-kernel-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-kernel@vger.kernel.org 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. 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. Signed-off-by: Saravana Kannan --- 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 + * @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 unmark waiting consumers + * + * 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. + */ +static void device_link_check_waiting_consumers(void) +{ + 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); + 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. + */ + 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(); 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. + */ + 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. + * + * 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. * @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); 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. * @status: Driver status information. */ struct dev_links_info { struct list_head suppliers; struct list_head consumers; + struct list_head needs_suppliers; enum dl_dev_state status; }; -- 2.22.0.709.g102302147b-goog