From patchwork Wed Sep 4 21:11:20 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Saravana Kannan X-Patchwork-Id: 11131665 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BB8E51399 for ; Wed, 4 Sep 2019 21:11:39 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 9B0A722CED for ; Wed, 4 Sep 2019 21:11:39 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="PeL1qXyM" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730800AbfIDVLf (ORCPT ); Wed, 4 Sep 2019 17:11:35 -0400 Received: from mail-qk1-f201.google.com ([209.85.222.201]:54720 "EHLO mail-qk1-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729888AbfIDVLf (ORCPT ); Wed, 4 Sep 2019 17:11:35 -0400 Received: by mail-qk1-f201.google.com with SMTP id v143so10329670qka.21 for ; Wed, 04 Sep 2019 14:11:34 -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=fqo1KOf+cOCC74wEKfOYxznTOIvWMgdfyPycEAYi4u0=; b=PeL1qXyMOH//w9FqqtXrPr0U/fbDTH8pM+CFv9qRRvE17R9i8phJPFcYx2wFFxFpmT L2cbFIIwQDA9YrgppFjttAjSOEOmhICQmJvE4W6EA7QqG0nO9pkamwBxhvOuK90B+pr0 4iDR4xDi7PX1bC/hzHDEQeyS+3t4BvNnwAM8YxVQVoaYJ9c2UdwvJ6Vb1kf74DBQXsmC hHFEqPWNoPKLNtORfG0uAOqBI2Xdt8R4U2cEXo7CX2dmMTLq5Qq5yUNvCK5W/zmE6OS8 rHPGwU/JAiWewE7cAyguYpreK9lmidgwwFxNgnYattgdi5WJE2fSoA6hFE8RX40f47XT 5VtQ== 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=fqo1KOf+cOCC74wEKfOYxznTOIvWMgdfyPycEAYi4u0=; b=EkEysQPF1TEqJmvx/6Lk5ieA+CN8NY5gO3GkHp2Go46DyamBabo8DSL6j35h2dOtb8 nfdNKkNustcPaOLryY8cGxZzyeuxg8SLMlB5IqXxKu1wd+1VD1IpZUZXlCg4mhoDoZLL EomiCaYIPJTKeHBEJADpHxUNo4DWspboZTkmkurSaIaHID3JeME4b3DUYsP4AbAwKXG4 yVzbPXUvtBHfSVRNH3gZGw/+F8NCpvdq0JE5R3j1/DJXjuDqid1sLdPu4RBNakhrv7aM +clJblxLEoKkS0Kr2nJ7+D3tMztnZ0ed3185RiwrNYKcp4xrdio3t8QV/qSTfFP4g1ty lptg== X-Gm-Message-State: APjAAAXyqwfsRXsAmfOz/v4z0P1aGstTRjDENsomLNxSpQtGunbImXGm aOBIlVioul5zppehYNrHMDD/t5FOCVHMiGE= X-Google-Smtp-Source: APXvYqxLEwDRwPCj/iR1Fw4FryEYrIIw49FmN69S5sYNfxhbYpfvQtinGUDL7+sP4kfmz2sV10P7WzBguuQ7Uu0= X-Received: by 2002:a0c:8402:: with SMTP id l2mr12315654qva.201.1567631494059; Wed, 04 Sep 2019 14:11:34 -0700 (PDT) Date: Wed, 4 Sep 2019 14:11:20 -0700 In-Reply-To: <20190904211126.47518-1-saravanak@google.com> Message-Id: <20190904211126.47518-2-saravanak@google.com> Mime-Version: 1.0 References: <20190904211126.47518-1-saravanak@google.com> X-Mailer: git-send-email 2.23.0.187.g17f5b7556c-goog Subject: [PATCH v11 1/6] driver core: Add fwnode_to_dev() to look up device from fwnode From: Saravana Kannan To: Rob Herring , Mark Rutland , Greg Kroah-Hartman , "Rafael J. Wysocki" , Frank Rowand , Jonathan Corbet , Len Brown Cc: Saravana Kannan , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-acpi@vger.kernel.org, clang-built-linux@googlegroups.com, David Collins , kernel-team@android.com Sender: linux-acpi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-acpi@vger.kernel.org It's often useful to look up a device that corresponds to a fwnode. So add an API to do that irrespective of the bus on which the device has been added to. Signed-off-by: Saravana Kannan --- drivers/base/core.c | 7 +++++++ include/linux/fwnode.h | 2 ++ 2 files changed, 9 insertions(+) diff --git a/drivers/base/core.c b/drivers/base/core.c index 2db62d98e395..510fabf8918c 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -2198,6 +2198,10 @@ int device_add(struct device *dev) BUS_NOTIFY_ADD_DEVICE, dev); kobject_uevent(&dev->kobj, KOBJ_ADD); + + if (dev->fwnode && !dev->fwnode->dev) + dev->fwnode->dev = dev; + bus_probe_device(dev); if (parent) klist_add_tail(&dev->p->knode_parent, @@ -2342,6 +2346,9 @@ void device_del(struct device *dev) kill_device(dev); device_unlock(dev); + if (dev->fwnode && dev->fwnode->dev == dev) + dev->fwnode->dev = NULL; + /* Notify clients of device removal. This call must come * before dpm_sysfs_remove(). */ diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index ababd6bc82f3..d8c6d231d577 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -17,6 +17,7 @@ struct device; struct fwnode_handle { struct fwnode_handle *secondary; const struct fwnode_operations *ops; + struct device *dev; }; /** @@ -123,5 +124,6 @@ struct fwnode_operations { if (fwnode_has_op(fwnode, op)) \ (fwnode)->ops->op(fwnode, ## __VA_ARGS__); \ } while (false) +#define get_dev_from_fwnode(fwnode) get_device((fwnode)->dev) #endif From patchwork Wed Sep 4 21:11:21 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Saravana Kannan X-Patchwork-Id: 11131667 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 35A671399 for ; Wed, 4 Sep 2019 21:11:42 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 0433622CF5 for ; Wed, 4 Sep 2019 21:11:42 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="I7iAwvNy" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730828AbfIDVLj (ORCPT ); Wed, 4 Sep 2019 17:11:39 -0400 Received: from mail-pf1-f202.google.com ([209.85.210.202]:37293 "EHLO mail-pf1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730814AbfIDVLj (ORCPT ); Wed, 4 Sep 2019 17:11:39 -0400 Received: by mail-pf1-f202.google.com with SMTP id g1so118930pfo.4 for ; Wed, 04 Sep 2019 14:11:38 -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=EIXukcSdp+uxpQofKUUVCp+AI9LAzuFCZNBqIe/4iSA=; b=I7iAwvNy0BYDx2Hd04bs57afJVCn40P2S+5F/81N2jzkNthhOFXdnOvY62qFcRz49/ oijEo8evGiM9cQ6sYLAWBkct9hcX7SLyUcO2Psea6nSJb96zy/UEsQtGte3dDSkdMlqT sNmv3Q/A/LK8pVkBdfr0rHKZ9VO6lqIY3wAs9PaD1IuWGPDhQzg9dAxciSDxuGVK9OYx 2rXLFDFkuu5LR97CUHVpbC15OfwPBiiMsslDdbAxnl0satjE9QuA6yK4LCMlN9cCzVP5 7mLVTVEucHr4zQPQe+3Ygz7XM7QI4oH1b+uuUUBzOCdbv7nN6BrwLqQhwomapXtrqFOW gPBA== 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=EIXukcSdp+uxpQofKUUVCp+AI9LAzuFCZNBqIe/4iSA=; b=Q05KVt75yBzzmJbDpN9QwgwA8+9VLNBx+fVth2dd7K0yvfGuSAUaitlt+J15C68FdZ DTRg96zq1Spvn9l0gBNV8oMxqiWU2knvMlwG1YKq5Fa8CfyAWO+ci7HVNg3BZvKheria Joc7aLouPkuJVYNQbgSUB79Tvv73D3RD8UzA8XBOMdoCvP5RBUMoLDctVerZJte/J0k+ JtE2t1flWKWjjuOxNm6S5X/odvg9EIgYP5HJPNOhMWl5zBi25ztu+nY7fwJj19iyB4Yw 6gy/etLCGG2HGY35mbenVCL/epcEH6e31Zfa7ar2uREQEVzqeoQ5W18QpRhw1CPG/c67 f/nw== X-Gm-Message-State: APjAAAWvsnISB7CfS2BqJayKnECCqg2dnplkFmksHjF2XvtucRqtIov8 NKqHlcdL7guQNPRx2D4XAIYaZaVSezrgd18= X-Google-Smtp-Source: APXvYqytALag+/pT4qSP38GJ/ffVQu/rLmyGfhsRgC4DWn3Pjx5MKv1jN8asTya9rhoV/Pn1LrT/LhLGLhxcPgo= X-Received: by 2002:a63:2b84:: with SMTP id r126mr138422pgr.308.1567631497483; Wed, 04 Sep 2019 14:11:37 -0700 (PDT) Date: Wed, 4 Sep 2019 14:11:21 -0700 In-Reply-To: <20190904211126.47518-1-saravanak@google.com> Message-Id: <20190904211126.47518-3-saravanak@google.com> Mime-Version: 1.0 References: <20190904211126.47518-1-saravanak@google.com> X-Mailer: git-send-email 2.23.0.187.g17f5b7556c-goog Subject: [PATCH v11 2/6] 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 , Jonathan Corbet , Len Brown Cc: Saravana Kannan , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-acpi@vger.kernel.org, clang-built-linux@googlegroups.com, David Collins , kernel-team@android.com Sender: linux-acpi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-acpi@vger.kernel.org The firmware corresponding to a device (dev.fwnode) might be able to provide functional dependency information between a device and its supplier and consumer devices. Tracking this functional dependency allows optimizing device probe order and informing a supplier when all its consumers have probed (and thereby actively managing their resources). The existing device links feature allows tracking and using supplier-consumer relationships. So, this patch adds the add_links() fwnode callback to allow firmware to create device links for each device as the device is added. 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 --- Documentation/driver-api/device_link.rst | 3 +- drivers/base/core.c | 88 ++++++++++++++++++++++++ include/linux/device.h | 2 + include/linux/fwnode.h | 17 +++++ 4 files changed, 109 insertions(+), 1 deletion(-) diff --git a/Documentation/driver-api/device_link.rst b/Documentation/driver-api/device_link.rst index 1b5020ec6517..bc2d89af88ce 100644 --- a/Documentation/driver-api/device_link.rst +++ b/Documentation/driver-api/device_link.rst @@ -281,7 +281,8 @@ State machine :c:func:`driver_bound()`.) * Before a consumer device is probed, presence of supplier drivers is - verified by checking that links to suppliers are in ``DL_STATE_AVAILABLE`` + verified by checking the consumer device is not in the wait_for_suppliers + list and by checking that links to suppliers are in ``DL_STATE_AVAILABLE`` state. The state of the links is updated to ``DL_STATE_CONSUMER_PROBE``. (Call to :c:func:`device_links_check_suppliers()` from :c:func:`really_probe()`.) diff --git a/drivers/base/core.c b/drivers/base/core.c index 510fabf8918c..b3896da73b3d 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); @@ -430,6 +432,58 @@ struct device_link *device_link_add(struct device *consumer, } EXPORT_SYMBOL_GPL(device_link_add); +/** + * device_link_wait_for_supplier - Add device to wait_for_suppliers list + * @consumer: Consumer device + * + * Marks the @consumer device as waiting for suppliers to become available by + * adding it to the wait_for_suppliers list. The consumer device will never be + * probed until it's removed from the wait_for_suppliers list. + * + * The caller is responsible for adding the links to the supplier devices once + * they are available and removing the @consumer device from the + * wait_for_suppliers list once links to all the suppliers have been created. + * + * 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_add_missing_supplier_links - Add links from consumer devices to + * supplier devices, leaving any + * consumer with inactive suppliers on + * the wait_for_suppliers list + * + * Loops through all consumers waiting on suppliers and tries to add all their + * supplier links. If that succeeds, the consumer device is removed from + * wait_for_suppliers list. Otherwise, they are left in the wait_for_suppliers + * list. Devices left on the wait_for_suppliers list will not be probed. + * + * The fwnode add_links 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_add_missing_supplier_links(void) +{ + struct device *dev, *tmp; + + mutex_lock(&wfs_lock); + list_for_each_entry_safe(dev, tmp, &wait_for_suppliers, + links.needs_suppliers) + if (!fwnode_call_int_op(dev->fwnode, 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)) @@ -564,6 +618,17 @@ int device_links_check_suppliers(struct device *dev) struct device_link *link; int ret = 0; + /* + * Device waiting for supplier to become available is not allowed to + * probe. + */ + 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) { @@ -848,6 +913,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). @@ -1712,6 +1781,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); @@ -2202,6 +2272,24 @@ int device_add(struct device *dev) if (dev->fwnode && !dev->fwnode->dev) dev->fwnode->dev = dev; + /* + * 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_add_missing_supplier_links(); + + if (fwnode_has_op(dev->fwnode, add_links) + && fwnode_call_int_op(dev->fwnode, 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 f30e80185825..968316bb3bd1 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -1136,11 +1136,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; }; diff --git a/include/linux/fwnode.h b/include/linux/fwnode.h index d8c6d231d577..6ae05b9ce359 100644 --- a/include/linux/fwnode.h +++ b/include/linux/fwnode.h @@ -66,6 +66,21 @@ struct fwnode_reference_args { * endpoint node. * @graph_get_port_parent: Return the parent node of a port node. * @graph_parse_endpoint: Parse endpoint for port and endpoint id. + * @add_links: Called after the device corresponding to the fwnode is added + * using device_add(). 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 other + * devices are added to allow creating device links to any newly + * available suppliers. + * + * Return 0 if device links have been successfully created to all + * the suppliers of this device or if the supplier information is + * not known. Return an error if and only if the supplier + * information is known but some of the suppliers are not yet + * available to create device links to. */ struct fwnode_operations { struct fwnode_handle *(*get)(struct fwnode_handle *fwnode); @@ -103,6 +118,8 @@ struct fwnode_operations { (*graph_get_port_parent)(struct fwnode_handle *fwnode); int (*graph_parse_endpoint)(const struct fwnode_handle *fwnode, struct fwnode_endpoint *endpoint); + int (*add_links)(const struct fwnode_handle *fwnode, + struct device *dev); }; #define fwnode_has_op(fwnode, op) \ From patchwork Wed Sep 4 21:11:22 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Saravana Kannan X-Patchwork-Id: 11131675 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1091B1510 for ; Wed, 4 Sep 2019 21:11:57 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id DC33022CF7 for ; Wed, 4 Sep 2019 21:11:56 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="CJsbVD3O" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730876AbfIDVLq (ORCPT ); Wed, 4 Sep 2019 17:11:46 -0400 Received: from mail-yb1-f201.google.com ([209.85.219.201]:52768 "EHLO mail-yb1-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730842AbfIDVLm (ORCPT ); Wed, 4 Sep 2019 17:11:42 -0400 Received: by mail-yb1-f201.google.com with SMTP id n6so17903701ybf.19 for ; Wed, 04 Sep 2019 14:11:41 -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=r0SSGqejS7hGeDjkHLwcULsZg1wwhwR+jJOFpk7tqQw=; b=CJsbVD3OcHVO2BmTaHhLdM8R69DPvFiRNpgReK6ZbRstWbyKjnbhe6ob9xYbFOXw1d MgZqETZSg7fULSru2GFIe8C5Z2uapR4pGuS/6FFH3pCP7Wu7W7k2dMcwRFSmx/td64KR Pyhj32Z2IEUqzkydVkotEzEu2G51r7+D8QAkPZz8X64FQf124OQwAXHSvUGnGeVfONgb 2uDDjK3kqiHbCwAlnA8ypHK2+gT7peBGKfiNjNhHpKVGYnWg7jDOoQjolPY5JNKp5T4c zd9CKkkKohzS06bXCo950XxLkQCIsMSYbwy/Tpf8EqSRke3bSRy6kSiQC2FLfzIBPw9P NXnw== 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=r0SSGqejS7hGeDjkHLwcULsZg1wwhwR+jJOFpk7tqQw=; b=Y6KGQqreQHHEqcQZ1LqySXrHbVt4ll/taiErv21LiukbvSHtwznaCZAwDQOUgOZroD 3ofzM8TsUX9DSPWo3DsmiSXfVnZz43CduxjjpdLEN615/mejV62RCj/S+9bGR0IPR9RR QwMhvJQsXBUMyUBobwRwGf3y24xGtk/85eKdrFtka3L096QBxzRa92eSIv7ZBTXikuVs HeFHVNnVECT2LgNKtAj7SajFuiAQbndxhm0coj9mac8xJYJkffVzUR4R9CRlLrntZnEp pFyMia0KoYmSv3E/mKg5MmS4u2otPbyG3gNQ3yiBOW+dlPRqgQIRRn3t8/BUga+i2NDa mm4w== X-Gm-Message-State: APjAAAVEZAG4+FBIKofMOI5sM3hZ+Q1fCXUaZDya/lSxFFXOvksLIxAe INOSJtP92PuXhNy9iqvUSd1VvHgKy156ySs= X-Google-Smtp-Source: APXvYqw9DYuppXyE9DDBy3s7xj78/nOW7FpktecBcnUmZZTrDPZFDZFeneaB/uBYHKtgK/U/aBRFEykhiwWAWhw= X-Received: by 2002:a81:8391:: with SMTP id t139mr29431136ywf.182.1567631501027; Wed, 04 Sep 2019 14:11:41 -0700 (PDT) Date: Wed, 4 Sep 2019 14:11:22 -0700 In-Reply-To: <20190904211126.47518-1-saravanak@google.com> Message-Id: <20190904211126.47518-4-saravanak@google.com> Mime-Version: 1.0 References: <20190904211126.47518-1-saravanak@google.com> X-Mailer: git-send-email 2.23.0.187.g17f5b7556c-goog Subject: [PATCH v11 3/6] of: property: Add functional dependency link from DT bindings From: Saravana Kannan To: Rob Herring , Mark Rutland , Greg Kroah-Hartman , "Rafael J. Wysocki" , Frank Rowand , Jonathan Corbet , Len Brown Cc: Saravana Kannan , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-acpi@vger.kernel.org, clang-built-linux@googlegroups.com, David Collins , kernel-team@android.com, kbuild test robot Sender: linux-acpi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-acpi@vger.kernel.org Add device links after the devices are created (but before they are probed) by looking at common DT bindings like clocks and interconnects. Automatically adding device links for functional dependencies at the framework level provides the following benefits: - Optimizes device probe order and avoids the useless work of attempting probes of devices that will not probe successfully (because their suppliers aren't present or haven't probed yet). For example, in a commonly available mobile SoC, registering just one consumer device's driver at an initcall level earlier than the supplier device's driver causes 11 failed probe attempts before the consumer device probes successfully. This was with a kernel with all the drivers statically compiled in. This problem gets a lot worse if all the drivers are loaded as modules without direct symbol dependencies. - Supplier devices like clock providers, interconnect providers, etc need to keep the resources they provide active and at a particular state(s) during boot up even if their current set of consumers don't request the resource to be active. This is because the rest of the consumers might not have probed yet and turning off the resource before all the consumers have probed could lead to a hang or undesired user experience. Some frameworks (Eg: regulator) handle this today by turning off "unused" resources at late_initcall_sync and hoping all the devices have probed by then. This is not a valid assumption for systems with loadable modules. Other frameworks (Eg: clock) just don't handle this due to the lack of a clear signal for when they can turn off resources. This leads to downstream hacks to handle cases like this that can easily be solved in the upstream kernel. By linking devices before they are probed, we give suppliers a clear count of the number of dependent consumers. Once all of the consumers are active, the suppliers can turn off the unused resources without making assumptions about the number of consumers. By default we just add device-links to track "driver presence" (probe succeeded) of the supplier device. If any other functionality provided by device-links are needed, it is left to the consumer/supplier devices to change the link when they probe. kbuild test robot reported clang error about missing const Reported-by: kbuild test robot Signed-off-by: Saravana Kannan --- .../admin-guide/kernel-parameters.rst | 1 + .../admin-guide/kernel-parameters.txt | 6 + drivers/of/property.c | 241 ++++++++++++++++++ 3 files changed, 248 insertions(+) diff --git a/Documentation/admin-guide/kernel-parameters.rst b/Documentation/admin-guide/kernel-parameters.rst index d05d531b4ec9..6d421694d98e 100644 --- a/Documentation/admin-guide/kernel-parameters.rst +++ b/Documentation/admin-guide/kernel-parameters.rst @@ -127,6 +127,7 @@ parameter is applicable:: NET Appropriate network support is enabled. NUMA NUMA support is enabled. NFS Appropriate NFS support is enabled. + OF Devicetree is enabled. OSS OSS sound support is enabled. PV_OPS A paravirtualized kernel is enabled. PARIDE The ParIDE (parallel port IDE) subsystem is enabled. diff --git a/Documentation/admin-guide/kernel-parameters.txt b/Documentation/admin-guide/kernel-parameters.txt index af0a62af6fd8..e95f2a58acc5 100644 --- a/Documentation/admin-guide/kernel-parameters.txt +++ b/Documentation/admin-guide/kernel-parameters.txt @@ -3181,6 +3181,12 @@ This can be set from sysctl after boot. See Documentation/admin-guide/sysctl/vm.rst for details. + of_devlink [OF, KNL] Create device links between consumer and + supplier devices by scanning the devictree to infer the + consumer/supplier relationships. A consumer device + will not be probed until all the supplier devices have + probed successfully. + ohci1394_dma=early [HW] enable debugging via the ohci1394 driver. See Documentation/debugging-via-ohci1394.txt for more info. diff --git a/drivers/of/property.c b/drivers/of/property.c index d7fa75e31f22..23b5ee5b0570 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "of_private.h" @@ -985,6 +986,245 @@ of_fwnode_device_get_match_data(const struct fwnode_handle *fwnode, return of_device_get_match_data(dev); } +static bool of_is_ancestor_of(struct device_node *test_ancestor, + struct device_node *child) +{ + of_node_get(child); + while (child) { + if (child == test_ancestor) { + of_node_put(child); + return false; + } + child = of_get_next_parent(child); + } + return true; +} + +/** + * of_link_to_phandle - Add device link to supplier from supplier phandle + * @dev: consumer device + * @sup_np: phandle to supplier device tree node + * + * Given a phandle to a supplier device tree node (@sup_np), this function + * finds the device that owns the supplier device tree node and creates a + * device link from @dev consumer device to the supplier device. This function + * doesn't create device links for invalid scenarios such as trying to create a + * link with a parent device as the consumer of its child device. In such + * cases, it returns an error. + * + * Returns: + * - 0 if link successfully created to supplier + * - -EAGAIN if linking to the supplier should be reattempted + * - -EINVAL if the supplier link is invalid and should not be created + * - -ENODEV if there is no device that corresponds to the supplier phandle + */ +static int of_link_to_phandle(struct device *dev, struct device_node *sup_np) +{ + struct device *sup_dev; + u32 dl_flags = DL_FLAG_AUTOPROBE_CONSUMER; + int ret = 0; + struct device_node *tmp_np = sup_np; + + of_node_get(sup_np); + /* + * Find the device node that contains the supplier phandle. It may be + * @sup_np or it may be an ancestor of @sup_np. + */ + while (sup_np && !of_find_property(sup_np, "compatible", NULL)) + sup_np = of_get_next_parent(sup_np); + if (!sup_np) { + dev_dbg(dev, "Not linking to %pOFP - No device\n", tmp_np); + return -ENODEV; + } + + /* + * Don't allow linking a device node as a consumer of one of its + * descendant nodes. By definition, a child node can't be a functional + * dependency for the parent node. + */ + if (!of_is_ancestor_of(dev->of_node, sup_np)) { + dev_dbg(dev, "Not linking to %pOFP - is descendant\n", sup_np); + of_node_put(sup_np); + return -EINVAL; + } + sup_dev = get_dev_from_fwnode(&sup_np->fwnode); + of_node_put(sup_np); + if (!sup_dev) + return -EAGAIN; + if (!device_link_add(dev, sup_dev, dl_flags)) + ret = -EAGAIN; + put_device(sup_dev); + return ret; +} + +/** + * parse_prop_cells - Property parsing function for suppliers + * + * @np: Pointer to device tree node containing a list + * @prop_name: Name of property to be parsed. Expected to hold phandle values + * @index: For properties holding a list of phandles, this is the index + * into the list. + * @list_name: Property name that is known to contain list of phandle(s) to + * supplier(s) + * @cells_name: property name that specifies phandles' arguments count + * + * This is a helper function to parse properties that have a known fixed name + * and are a list of phandles and phandle arguments. + * + * Returns: + * - phandle node pointer with refcount incremented. Caller must of_node_put() + * on it when done. + * - NULL if no phandle found at index + */ +static struct device_node *parse_prop_cells(struct device_node *np, + const char *prop_name, int index, + const char *list_name, + const char *cells_name) +{ + struct of_phandle_args sup_args; + + if (strcmp(prop_name, list_name)) + return NULL; + + if (of_parse_phandle_with_args(np, list_name, cells_name, index, + &sup_args)) + return NULL; + + return sup_args.np; +} + +static struct device_node *parse_clocks(struct device_node *np, + const char *prop_name, int index) +{ + return parse_prop_cells(np, prop_name, index, "clocks", "#clock-cells"); +} + +static struct device_node *parse_interconnects(struct device_node *np, + const char *prop_name, int index) +{ + return parse_prop_cells(np, prop_name, index, "interconnects", + "#interconnect-cells"); +} + +static int strcmp_suffix(const char *str, const char *suffix) +{ + unsigned int len, suffix_len; + + len = strlen(str); + suffix_len = strlen(suffix); + if (len <= suffix_len) + return -1; + return strcmp(str + len - suffix_len, suffix); +} + +static struct device_node *parse_regulators(struct device_node *np, + const char *prop_name, int index) +{ + if (index || strcmp_suffix(prop_name, "-supply")) + return NULL; + + return of_parse_phandle(np, prop_name, 0); +} + +/** + * struct supplier_bindings - Property parsing functions for suppliers + * + * @parse_prop: function name + * parse_prop() finds the node corresponding to a supplier phandle + * @parse_prop.np: Pointer to device node holding supplier phandle property + * @parse_prop.prop_name: Name of property holding a phandle value + * @parse_prop.index: For properties holding a list of phandles, this is the + * index into the list + * + * Returns: + * parse_prop() return values are + * - phandle node pointer with refcount incremented. Caller must of_node_put() + * on it when done. + * - NULL if no phandle found at index + */ +struct supplier_bindings { + struct device_node *(*parse_prop)(struct device_node *np, + const char *prop_name, int index); +}; + +static const struct supplier_bindings bindings[] = { + { .parse_prop = parse_clocks, }, + { .parse_prop = parse_interconnects, }, + { .parse_prop = parse_regulators, }, + {}, +}; + +/** + * of_link_property - Create device links to suppliers listed in a property + * @dev: Consumer device + * @con_np: The consumer device tree node which contains the property + * @prop_name: Name of property to be parsed + * + * This function checks if the property @prop_name that is present in the + * @con_np device tree node is one of the known common device tree bindings + * that list phandles to suppliers. If @prop_name isn't one, this function + * doesn't do anything. + * + * If @prop_name is one, this function attempts to create device links from the + * consumer device @dev to all the devices of the suppliers listed in + * @prop_name. + * + * Any failed attempt to create a device link will NOT result in an immediate + * return. of_link_property() must create links to all the available supplier + * devices even when attempts to create a link to one or more suppliers fail. + */ +static int of_link_property(struct device *dev, struct device_node *con_np, + const char *prop_name) +{ + struct device_node *phandle; + const struct supplier_bindings *s = bindings; + unsigned int i = 0; + bool matched = false; + int ret = 0; + + /* Do not stop at first failed link, link all available suppliers. */ + while (!matched && s->parse_prop) { + while ((phandle = s->parse_prop(con_np, prop_name, i))) { + matched = true; + i++; + if (of_link_to_phandle(dev, phandle) == -EAGAIN) + ret = -EAGAIN; + of_node_put(phandle); + } + s++; + } + return ret; +} + +static int __of_link_to_suppliers(struct device *dev, + struct device_node *con_np) +{ + struct device_node *child; + struct property *p; + int ret = 0; + + for_each_property_of_node(con_np, p) + if (of_link_property(dev, con_np, p->name)) + ret = -EAGAIN; + + return ret; +} + +static bool of_devlink; +core_param(of_devlink, of_devlink, bool, 0); + +static int of_fwnode_add_links(const struct fwnode_handle *fwnode, + struct device *dev) +{ + if (!of_devlink) + return 0; + + if (unlikely(!is_of_node(fwnode))) + return 0; + + return __of_link_to_suppliers(dev, to_of_node(fwnode)); +} + const struct fwnode_operations of_fwnode_ops = { .get = of_fwnode_get, .put = of_fwnode_put, @@ -1001,5 +1241,6 @@ const struct fwnode_operations of_fwnode_ops = { .graph_get_remote_endpoint = of_fwnode_graph_get_remote_endpoint, .graph_get_port_parent = of_fwnode_graph_get_port_parent, .graph_parse_endpoint = of_fwnode_graph_parse_endpoint, + .add_links = of_fwnode_add_links, }; EXPORT_SYMBOL_GPL(of_fwnode_ops); From patchwork Wed Sep 4 21:11:23 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Saravana Kannan X-Patchwork-Id: 11131669 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id B9E8D14F7 for ; Wed, 4 Sep 2019 21:11:46 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 893F822CF5 for ; Wed, 4 Sep 2019 21:11:46 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="EtncdXs6" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730874AbfIDVLq (ORCPT ); Wed, 4 Sep 2019 17:11:46 -0400 Received: from mail-qt1-f202.google.com ([209.85.160.202]:56947 "EHLO mail-qt1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730844AbfIDVLp (ORCPT ); Wed, 4 Sep 2019 17:11:45 -0400 Received: by mail-qt1-f202.google.com with SMTP id m6so60062qtk.23 for ; Wed, 04 Sep 2019 14:11:44 -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=16o51+fRGAtBL1knYafYOFrACsjnlPcT58bue7EPSIc=; b=EtncdXs6xZksPOI9YvqtVg+R73USdHA5XxztdSm/lbDuhNyfKt5SEappQpRT7AuxMd XBZyQPhvM9bzFz/N2oijh/hRdJlBaFlZUlNeUshn65LyrALCGKDjWp2eOisSl97xSxOV 7vDFm2WtZU9AYB7LeW9chQFpCl/0EnNyOsYC+7ocxB176V1XGc7Wsv85ULRjxFf1tuZv iQfMpaFH/5to3bGrQSjrC/srj4V7OJm25Nt6YCk8n6jAZBkZG9tfpnBjI9q6kXOqyjfj jKshDlgj+kJtIJ+TKYIR2GMRa4Qvel9i6dTglwWPixgMxwGb9WsU/Q3XylEaQtaUGAbq WUlQ== 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=16o51+fRGAtBL1knYafYOFrACsjnlPcT58bue7EPSIc=; b=bxhABKH8eKroIDebBbK1oqxsYKRdBER2zx39dPzlkdgFrtPwCkB0fDZcbTpDJePOl2 IJ/kLalnrGfQDiARFASp4WpUgqH6ZbLJQzrYkOA83kQ195F+TvsNi8xDKJD3JUqmR77O wxJxP/fT0DHTtgfz6r5EUjqCP/pbuqD/3XTB4xsrm8VPcRp1/yz5M026/mXV47G+wE9z WwlEiAXoVZ6g1zk9DGBRPANfNeqAnomGnb5haoqDH3YElvSV+9A2BNp/d2qaGevAIPhf tHTX9ve1mk1uyCc7TNQw8Cb10VaRAWPgt1Ck1dMnZWZxn2W4XV7J3cP6uyzXXd+ykxw7 RdcQ== X-Gm-Message-State: APjAAAUFiKwZ0HP2V49NRdZzD575KeKG2mW6KQLz5FitajJGlzMPylCc QYLAOVShSFryRL6r+ItOz+pJBnUxQp+zcjA= X-Google-Smtp-Source: APXvYqxeYy0fVBxdj57QTyED9Vstu+M5eN0/UaBzsEvEaWPKrDzrCe5pkYhvfQiQLMEkh/SX2CCQMWrYE66rcUo= X-Received: by 2002:a0c:b5c7:: with SMTP id o7mr6416568qvf.225.1567631504283; Wed, 04 Sep 2019 14:11:44 -0700 (PDT) Date: Wed, 4 Sep 2019 14:11:23 -0700 In-Reply-To: <20190904211126.47518-1-saravanak@google.com> Message-Id: <20190904211126.47518-5-saravanak@google.com> Mime-Version: 1.0 References: <20190904211126.47518-1-saravanak@google.com> X-Mailer: git-send-email 2.23.0.187.g17f5b7556c-goog Subject: [PATCH v11 4/6] driver core: Add sync_state driver/bus callback From: Saravana Kannan To: Rob Herring , Mark Rutland , Greg Kroah-Hartman , "Rafael J. Wysocki" , Frank Rowand , Jonathan Corbet , Len Brown Cc: Saravana Kannan , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-acpi@vger.kernel.org, clang-built-linux@googlegroups.com, David Collins , kernel-team@android.com, kbuild test robot Sender: linux-acpi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-acpi@vger.kernel.org This sync_state driver/bus callback is called once all the consumers of a supplier have probed successfully. This allows the supplier device's driver/bus to sync the supplier device's state to the software state with the guarantee that all the consumers are actively managing the resources provided by the supplier device. To maintain backwards compatibility and ease transition from existing frameworks and resource cleanup schemes, late_initcall_sync is the earliest when the sync_state callback might be called. There is no upper bound on the time by which the sync_state callback has to be called. This is because if a consumer device never probes, the supplier has to maintain its resources in the state left by the bootloader. For example, if the bootloader leaves the display backlight at a fixed voltage and the backlight driver is never probed, you don't want the backlight to ever be turned off after boot up. Also, when multiple devices are added after kernel init, some suppliers could be added before their consumer devices get added. In these instances, the supplier devices could get their sync_state callback called right after they probe because the consumers devices haven't had a chance to create device links to the suppliers. To handle this correctly, this change also provides APIs to pause/resume sync state callbacks so that when multiple devices are added, their sync_state callback evaluation can be postponed to happen after all of them are added. kbuild test robot reported missing documentation for device.state_synced Reported-by: kbuild test robot Signed-off-by: Saravana Kannan --- drivers/base/core.c | 72 ++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 24 ++++++++++++++ 2 files changed, 96 insertions(+) diff --git a/drivers/base/core.c b/drivers/base/core.c index b3896da73b3d..acbf0b1414ab 100644 --- a/drivers/base/core.c +++ b/drivers/base/core.c @@ -46,6 +46,8 @@ early_param("sysfs.deprecated", sysfs_deprecated_setup); /* Device links support. */ static LIST_HEAD(wait_for_suppliers); static DEFINE_MUTEX(wfs_lock); +static LIST_HEAD(deferred_sync); +static unsigned int defer_sync_state_count = 1; #ifdef CONFIG_SRCU static DEFINE_MUTEX(device_links_lock); @@ -648,6 +650,69 @@ int device_links_check_suppliers(struct device *dev) return ret; } +static void __device_links_supplier_sync_state(struct device *dev) +{ + struct device_link *link; + + if (dev->state_synced) + return; + + list_for_each_entry(link, &dev->links.consumers, s_node) { + if (!(link->flags & DL_FLAG_MANAGED)) + continue; + if (link->status != DL_STATE_ACTIVE) + return; + } + + if (dev->bus->sync_state) + dev->bus->sync_state(dev); + else if (dev->driver && dev->driver->sync_state) + dev->driver->sync_state(dev); + + dev->state_synced = true; +} + +void device_links_supplier_sync_state_pause(void) +{ + device_links_write_lock(); + defer_sync_state_count++; + device_links_write_unlock(); +} + +void device_links_supplier_sync_state_resume(void) +{ + struct device *dev, *tmp; + + device_links_write_lock(); + if (!defer_sync_state_count) { + WARN(true, "Unmatched sync_state pause/resume!"); + goto out; + } + defer_sync_state_count--; + if (defer_sync_state_count) + goto out; + + list_for_each_entry_safe(dev, tmp, &deferred_sync, links.defer_sync) { + __device_links_supplier_sync_state(dev); + list_del_init(&dev->links.defer_sync); + } +out: + device_links_write_unlock(); +} + +static int sync_state_resume_initcall(void) +{ + device_links_supplier_sync_state_resume(); + return 0; +} +late_initcall(sync_state_resume_initcall); + +static void __device_links_supplier_defer_sync(struct device *sup) +{ + if (list_empty(&sup->links.defer_sync)) + list_add_tail(&sup->links.defer_sync, &deferred_sync); +} + /** * device_links_driver_bound - Update device links after probing its driver. * @dev: Device to update the links for. @@ -692,6 +757,11 @@ void device_links_driver_bound(struct device *dev) WARN_ON(link->status != DL_STATE_CONSUMER_PROBE); WRITE_ONCE(link->status, DL_STATE_ACTIVE); + + if (defer_sync_state_count) + __device_links_supplier_defer_sync(link->supplier); + else + __device_links_supplier_sync_state(link->supplier); } dev->links.status = DL_DEV_DRIVER_BOUND; @@ -808,6 +878,7 @@ void device_links_driver_cleanup(struct device *dev) WRITE_ONCE(link->status, DL_STATE_DORMANT); } + list_del_init(&dev->links.defer_sync); __device_links_no_driver(dev); device_links_write_unlock(); @@ -1782,6 +1853,7 @@ void device_initialize(struct device *dev) INIT_LIST_HEAD(&dev->links.consumers); INIT_LIST_HEAD(&dev->links.suppliers); INIT_LIST_HEAD(&dev->links.needs_suppliers); + INIT_LIST_HEAD(&dev->links.defer_sync); dev->links.status = DL_DEV_NO_DRIVER; } EXPORT_SYMBOL_GPL(device_initialize); diff --git a/include/linux/device.h b/include/linux/device.h index 968316bb3bd1..9f2f2e169f95 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -81,6 +81,13 @@ extern void bus_remove_file(struct bus_type *, struct bus_attribute *); * that generate uevents to add the environment variables. * @probe: Called when a new device or driver add to this bus, and callback * the specific driver's probe to initial the matched device. + * @sync_state: Called to sync device state to software state after all the + * state tracking consumers linked to this device (present at + * the time of late_initcall) have successfully bound to a + * driver. If the device has no consumers, this function will + * be called at late_initcall_sync level. If the device has + * consumers that are never bound to a driver, this function + * will never get called until they do. * @remove: Called when a device removed from this bus. * @shutdown: Called at shut-down time to quiesce the device. * @@ -124,6 +131,7 @@ struct bus_type { int (*match)(struct device *dev, struct device_driver *drv); int (*uevent)(struct device *dev, struct kobj_uevent_env *env); int (*probe)(struct device *dev); + void (*sync_state)(struct device *dev); int (*remove)(struct device *dev); void (*shutdown)(struct device *dev); @@ -341,6 +349,13 @@ enum probe_type { * @probe: Called to query the existence of a specific device, * whether this driver can work with it, and bind the driver * to a specific device. + * @sync_state: Called to sync device state to software state after all the + * state tracking consumers linked to this device (present at + * the time of late_initcall) have successfully bound to a + * driver. If the device has no consumers, this function will + * be called at late_initcall_sync level. If the device has + * consumers that are never bound to a driver, this function + * will never get called until they do. * @remove: Called when the device is removed from the system to * unbind a device from this driver. * @shutdown: Called at shut-down time to quiesce the device. @@ -380,6 +395,7 @@ struct device_driver { const struct acpi_device_id *acpi_match_table; int (*probe) (struct device *dev); + void (*sync_state)(struct device *dev); int (*remove) (struct device *dev); void (*shutdown) (struct device *dev); int (*suspend) (struct device *dev, pm_message_t state); @@ -1137,12 +1153,14 @@ enum dl_dev_state { * @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. + * @defer_sync: Hook to global list of devices that have deferred sync_state. * @status: Driver status information. */ struct dev_links_info { struct list_head suppliers; struct list_head consumers; struct list_head needs_suppliers; + struct list_head defer_sync; enum dl_dev_state status; }; @@ -1218,6 +1236,9 @@ struct dev_links_info { * @offline: Set after successful invocation of bus type's .offline(). * @of_node_reused: Set if the device-tree node is shared with an ancestor * device. + * @state_synced: The hardware state of this device has been synced to match + * the software state of this device by calling the driver/bus + * sync_state() callback. * @dma_coherent: this particular device is dma coherent, even if the * architecture supports non-coherent devices. * @@ -1314,6 +1335,7 @@ struct device { bool offline_disabled:1; bool offline:1; bool of_node_reused:1; + bool state_synced:1; #if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU) || \ defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_CPU_ALL) @@ -1656,6 +1678,8 @@ struct device_link *device_link_add(struct device *consumer, struct device *supplier, u32 flags); void device_link_del(struct device_link *link); void device_link_remove(void *consumer, struct device *supplier); +void device_links_supplier_sync_state_pause(void); +void device_links_supplier_sync_state_resume(void); #ifdef CONFIG_DEVICE_NOTIFICATIONS extern void post_device_notification(struct watch_notification *n, u64 id); From patchwork Wed Sep 4 21:11:24 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Saravana Kannan X-Patchwork-Id: 11131671 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id C06BC14F7 for ; Wed, 4 Sep 2019 21:11:49 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 9F8E523402 for ; Wed, 4 Sep 2019 21:11:49 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="YRrXlCAV" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730904AbfIDVLs (ORCPT ); Wed, 4 Sep 2019 17:11:48 -0400 Received: from mail-pf1-f201.google.com ([209.85.210.201]:34771 "EHLO mail-pf1-f201.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730893AbfIDVLs (ORCPT ); Wed, 4 Sep 2019 17:11:48 -0400 Received: by mail-pf1-f201.google.com with SMTP id i2so130565pfe.1 for ; Wed, 04 Sep 2019 14:11:47 -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=BNYZzlx9aTQ+LCgQZ8IG6mRelU0sVitbFkM6D5hbnnk=; b=YRrXlCAVnzOOK2AfKz9blA2WPrjNpEFwO6Leu6qgi/oZvs/DH9zgLemvWVbfxKWe0s YCxi/y1Mm7LjwnUr7HhL0vUqE/tbkSq31IsqlVHfvBPhSpEC4XwJGtllRI+Aph4kHhz5 w35aCIKvqsH6vrHDhJYqLhBJmU0Py9hgPWxl5VpPKN8gMj2iaNMdX1oqh9SuENVPRlE0 vuL7OAgySwzO1pszYEoAHk5QI404dziPwjaKEx5Dp0Ys9UTaBYjuXGHRiXIDK4HZQuTs 97JdZlBhmpkBtfEIiemSRvuK+M7OqFzBYTaVLRdKXAsDFNHkmFldBvv6wGYmveq1NB66 cqlg== 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=BNYZzlx9aTQ+LCgQZ8IG6mRelU0sVitbFkM6D5hbnnk=; b=h/75oK3WjRuE16bsFgHewwt+ibNFHrUU5TOJu9Puzv5si/TJeHyjcRhpXsZeZC4uq3 G8s8wEvy2At7K5ERTK0vCBCr7YFUKCRog7ZHcth29TRwow3KtZKihEMr+ApLBlZBzzSJ Z+uVVPUnmlGWt+OAfm/ZcniVjGcSDQskY+7t8h33+F2GO+7UeB3HWWDPpx0/5WiWSm17 hezi/KDHcJ7wI9lntR6Vhp/7kdx0+zio6R/178pUtddKjISq8DVW0f1ARoDpdfA/frff jwXfihqXZMml3hRzSbXIEAn+zzGL8DP+yWHlxUlHG7nJbMbbU+GEFNn+SjpRN0TnQ6Bf HItA== X-Gm-Message-State: APjAAAV18vIeAaeE7zfqmlH7ZDv+W+L9bjPykXVq2z9LDxOn1B+Pgc0g iE0VQK0bdDQ87h8CMXjWeomlPUWxj/lINIA= X-Google-Smtp-Source: APXvYqxa0Q445BT0USKWHcd+K+nDsZIoXWF5ccMgBrzd0E7J8jAJCvSVyiJOr/BJ7oiKfX/XVA1lQD7iaFaxsQU= X-Received: by 2002:a63:2a08:: with SMTP id q8mr101014pgq.415.1567631507124; Wed, 04 Sep 2019 14:11:47 -0700 (PDT) Date: Wed, 4 Sep 2019 14:11:24 -0700 In-Reply-To: <20190904211126.47518-1-saravanak@google.com> Message-Id: <20190904211126.47518-6-saravanak@google.com> Mime-Version: 1.0 References: <20190904211126.47518-1-saravanak@google.com> X-Mailer: git-send-email 2.23.0.187.g17f5b7556c-goog Subject: [PATCH v11 5/6] of/platform: Pause/resume sync state during init and of_platform_populate() From: Saravana Kannan To: Rob Herring , Mark Rutland , Greg Kroah-Hartman , "Rafael J. Wysocki" , Frank Rowand , Jonathan Corbet , Len Brown Cc: Saravana Kannan , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-acpi@vger.kernel.org, clang-built-linux@googlegroups.com, David Collins , kernel-team@android.com Sender: linux-acpi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-acpi@vger.kernel.org When all the top level devices are populated from DT during kernel init, the supplier devices could be added and probed before the consumer devices are added and linked to the suppliers. To avoid the sync_state() callback from being called prematurely, pause the sync_state() callbacks before populating the devices and resume them at late_initcall_sync(). Similarly, when children devices are populated from a module using of_platform_populate(), there could be supplier-consumer dependencies between the children devices that are populated. To avoid the same problem with sync_state() being called prematurely, pause and resume sync_state() callbacks across of_platform_populate(). Signed-off-by: Saravana Kannan --- drivers/of/platform.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/of/platform.c b/drivers/of/platform.c index b47a2292fe8e..d93891a05f60 100644 --- a/drivers/of/platform.c +++ b/drivers/of/platform.c @@ -480,6 +480,7 @@ int of_platform_populate(struct device_node *root, pr_debug("%s()\n", __func__); pr_debug(" starting at: %pOF\n", root); + device_links_supplier_sync_state_pause(); for_each_child_of_node(root, child) { rc = of_platform_bus_create(child, matches, lookup, parent, true); if (rc) { @@ -487,6 +488,8 @@ int of_platform_populate(struct device_node *root, break; } } + device_links_supplier_sync_state_resume(); + of_node_set_flag(root, OF_POPULATED_BUS); of_node_put(root); @@ -518,6 +521,7 @@ static int __init of_platform_default_populate_init(void) if (!of_have_populated_dt()) return -ENODEV; + device_links_supplier_sync_state_pause(); /* * Handle certain compatibles explicitly, since we don't want to create * platform_devices for every node in /reserved-memory with a @@ -538,6 +542,14 @@ static int __init of_platform_default_populate_init(void) return 0; } arch_initcall_sync(of_platform_default_populate_init); + +static int __init of_platform_sync_state_init(void) +{ + if (of_have_populated_dt()) + device_links_supplier_sync_state_resume(); + return 0; +} +late_initcall_sync(of_platform_sync_state_init); #endif int of_platform_device_destroy(struct device *dev, void *data) From patchwork Wed Sep 4 21:11:25 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Saravana Kannan X-Patchwork-Id: 11131673 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BB2251399 for ; Wed, 4 Sep 2019 21:11:56 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 9C08923404 for ; Wed, 4 Sep 2019 21:11:56 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=google.com header.i=@google.com header.b="TH2xF9mp" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730931AbfIDVLw (ORCPT ); Wed, 4 Sep 2019 17:11:52 -0400 Received: from mail-pf1-f202.google.com ([209.85.210.202]:44854 "EHLO mail-pf1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1730925AbfIDVLv (ORCPT ); Wed, 4 Sep 2019 17:11:51 -0400 Received: by mail-pf1-f202.google.com with SMTP id b204so104870pfb.11 for ; Wed, 04 Sep 2019 14:11:51 -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=AONGf/XrwQ57qLLsqc1IAcmsTXCE/q6wdhnpClqGQ04=; b=TH2xF9mpWDfyKefZivaec2xL2ZLzp5qmn+OX6MYUIXS2P1CjEvnb+Hi+fFGGne37r+ n5WaCkirA8ys3qam6eq2w/K2LobHixeNannHiGYWMdrhwT5IqXtIrGF/Fq4ThsEtaE0M 6zMs2g088BP5ODUSxygXiKvEVb4ipDr1UDih7zeBZTcrmwVwnUx7v9C87QnD9SzyRexJ lgx7s3bLVkyvLu/ssnM7MB/bNODRixypQRgxHumSL3SpWLpiXre6kSIQ1fy8Cyg4KZSV OzI9XFXbFmzcF+hI9066HuVIAREQztV4usQa43BAXNW5jZmLD7g4BYm0On05mKRRN4JS uJ+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=AONGf/XrwQ57qLLsqc1IAcmsTXCE/q6wdhnpClqGQ04=; b=TZ27V0UHHz7ZxEI99J6gcu6IqcSIYcaB+fwUKJnpm2k8NeccuLF4q9ldxsWLlxhVqV mWKRwFHVLZRszCmlfNKsmDrJ832fXSvDY0K8liKmQcXXJpYKKjJL9Q92TK/iNoU5TU0N xgYPa8qsuA9vvm76sD6kP8TmGWjuuYxg1azH7W+8nrTrpPFcaFSNBsge5xCuKi4hXINn eP+Sv96ncYI6wLKyEReGv+91lD+TtPciDMD0KWzmtdqLQazeH8mDncRpIhlLexTWuwz1 vsGlCHTAb6BNgMXY5r2KhWYllqEwO4WmAhhXe7eTH7nq8pwWrMtaQVa0ssWMwzDxaXcJ NF9A== X-Gm-Message-State: APjAAAWWhBgKxEkQSKSkc9yxIPSzsqZLlURk1vhF6Fx+7DalKXeOa/EL hwal3Gny2y6GmsSVS8Xml5PNLs0aX4oAV2w= X-Google-Smtp-Source: APXvYqySn9/+gjnHwwNQKP3hpZKLo+bE+U3qbKQShvLUPwIPN5cMyBzHW3ZdrhgFeLsSv9k5msga7lFRlYMUPrY= X-Received: by 2002:a63:6f8f:: with SMTP id k137mr146678pgc.90.1567631510692; Wed, 04 Sep 2019 14:11:50 -0700 (PDT) Date: Wed, 4 Sep 2019 14:11:25 -0700 In-Reply-To: <20190904211126.47518-1-saravanak@google.com> Message-Id: <20190904211126.47518-7-saravanak@google.com> Mime-Version: 1.0 References: <20190904211126.47518-1-saravanak@google.com> X-Mailer: git-send-email 2.23.0.187.g17f5b7556c-goog Subject: [PATCH v11 6/6] of: property: Create device links for all child-supplier depencencies From: Saravana Kannan To: Rob Herring , Mark Rutland , Greg Kroah-Hartman , "Rafael J. Wysocki" , Frank Rowand , Jonathan Corbet , Len Brown Cc: Saravana Kannan , devicetree@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, linux-acpi@vger.kernel.org, clang-built-linux@googlegroups.com, David Collins , kernel-team@android.com Sender: linux-acpi-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-acpi@vger.kernel.org A parent device can have child devices that it adds when it probes. But this probing of the parent device can happen way after kernel init is done -- for example, when the parent device's driver is loaded as a module. In such cases, if the child devices depend on a supplier in the system, we need to make sure the supplier gets the sync_state() callback only after these child devices are added and probed. To achieve this, when creating device links for a device by looking at its DT node, don't just look at DT references at the top node level. Look at DT references in all the descendant nodes too and create device links from the ancestor device to all these supplier devices. This way, when the parent device probes and adds child devices, the child devices can then create their own device links to the suppliers and further delay the supplier's sync_state() callback to after the child devices are probed. Example: In this illustration, -> denotes DT references and indentation represents child status. Device node A Device node B -> D Device node C -> B, D Device node D Assume all these devices have their drivers loaded as modules. Without this patch, this is the sequence of events: 1. D is added. 2. A is added. 3. Device D probes. 4. Device D gets its sync_state() callback. 5. Device B and C might malfunction because their resources got altered/turned off before they can make active requests for them. With this patch, this is the sequence of events: 1. D is added. 2. A is added and creates device links to D. 3. Device link from A to B is not added because A is a parent of B. 4. Device D probes. 5. Device D does not get it's sync_state() callback because consumer A hasn't probed yet. 5. Device A probes. 5. a. Devices B and C are added. 5. b. Device links from B and C to D are added. 5. c. Device A's probe completes. 6. Device D does not get it's sync_state() callback because consumer A has probed but consumers B and C haven't probed yet. 7. Device B and C probe. 8. Device D gets it's sync_state() callback because all its consumers have probed. 9. None of the devices malfunction. Signed-off-by: Saravana Kannan --- drivers/of/property.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/of/property.c b/drivers/of/property.c index 23b5ee5b0570..923d6f88a99c 100644 --- a/drivers/of/property.c +++ b/drivers/of/property.c @@ -1207,6 +1207,10 @@ static int __of_link_to_suppliers(struct device *dev, if (of_link_property(dev, con_np, p->name)) ret = -EAGAIN; + for_each_child_of_node(con_np, child) + if (__of_link_to_suppliers(dev, child)) + ret = -EAGAIN; + return ret; }