Received: by 2002:a25:8b12:0:0:0:0:0 with SMTP id i18csp1935063ybl; Thu, 29 Aug 2019 00:48:16 -0700 (PDT) X-Google-Smtp-Source: APXvYqy1bQ12LKErXEBSophS57/VDcLVooStps2FnhQxD58IHueLK6OH/Ybt7UnxAfW67N1NSORE X-Received: by 2002:a17:90b:14c:: with SMTP id em12mr8192008pjb.22.1567064896119; Thu, 29 Aug 2019 00:48:16 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1567064896; cv=none; d=google.com; s=arc-20160816; b=pFfidfWKIHm37NZr36p3LUoO5BivftNtbJlgGHIQuBzpeT6IBKddQwxhx9CUcQI0B/ ZN4RkIq7Feqpol84zNuwB7p25LSIWfgQ5y/B3t1+ENqM7oZlYI/9zU4CXoFq8YWvdoDa LMmV9NvuvSUZMS7HD+ytd8fDoCK/dtC/TOOO1Rs+KSCGDjMba6XZb9zQ9aWifv9Q90t8 kS93s5K9sU/+k1P0Eoy+GcbLeEKskzKoFvyTnEfntnSUG3XqPVyHsZuQqY9ommQfATNq zDXhAlHLbYATnqKUfsbd6WHHc5e0+wSi3hPusptB0kyKfKyUUqT/zQNjqFbmKDy4uPGI LbfA== 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=clQn5smHaXibZ7cDcawjYf/96GrLwIoU4JeSrK4yhyY=; b=APTGyKsRWsS4dKCICJIxp/Hww/EbjtWyRRsAsSJ2gl3Ly9VcbqlCGqefBxSyDUa/0o w4qMssBQgTEcVfzQ55g/3paRnxaKalRsQIP1TPH7moRQSvWEdWXbQEs+NHgyDCfNdfOb QRqZC3IF6wzPh5p4GbySuics9J1rJcmCNPtbU3LNCzg7SfBNfEJXNdOQ6Inp0G5UN4Y6 eRFpjcyB2yBaAYXPn2aZOoFe6HKxEUBYlhC+qLu2YRXMaQZYA1+P3vej1kWD9Zu59aAF DSLYrpF8tQBuju1tmZ5tcPQ1RDox4PjeytsLgbR33Iz1BZfnzP5gfBKjDsT3B5ePairn dRvw== ARC-Authentication-Results: i=1; mx.google.com; dkim=pass header.i=@google.com header.s=20161025 header.b="H/wSFEaN"; 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 d7si1458424pjw.103.2019.08.29.00.48.00; Thu, 29 Aug 2019 00:48:16 -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="H/wSFEaN"; 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 S1727883AbfH2Hqm (ORCPT + 99 others); Thu, 29 Aug 2019 03:46:42 -0400 Received: from mail-pl1-f202.google.com ([209.85.214.202]:51373 "EHLO mail-pl1-f202.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727936AbfH2HqT (ORCPT ); Thu, 29 Aug 2019 03:46:19 -0400 Received: by mail-pl1-f202.google.com with SMTP id p9so1521996pls.18 for ; Thu, 29 Aug 2019 00:46:19 -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=clQn5smHaXibZ7cDcawjYf/96GrLwIoU4JeSrK4yhyY=; b=H/wSFEaNTzfxMfVauYdB7dnIL8vsVdbeDbhJ6kHtWqcYnM/H6arnA6Nlku7X9oX0Zf 9VccFU0voIdbLjlrKL1x9GwGVOpI8g1EkbP7tDyvOBN8GK5kfitiuneBW9PKqGyNn+KD dn7nRNX5tYCR4YoKPg8JJGske5qXI2zPQpTxro0SZZ4Ol+cgQmdZM9Tmpv1sc98h1X+R VSTpIAsJqpm/xZ5f4I6V0+eIXowTgbdi37onvs8+ZKMl3NMycJLeV5JIYkHv0HaHpeBP woTs0UOyDq3qkPL5+ZN49E53bd+bvx3jK5ID/UrZuck27VaesCr/lX9k7cHCwsXFUKtq d0yQ== 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=clQn5smHaXibZ7cDcawjYf/96GrLwIoU4JeSrK4yhyY=; b=t8OqUgE3rx5BScCRrCDm89MeppzkX7ZJAYQLAPGEX5Jk0+eE/HnlGMzMdSjvjSw58G PlD5YZOTX6kBKjm+d6Y3pk252ZC9tb6rWwHPvK/xjgbMyuYn1Qh3aOoNWMHndq0V0tk0 rJojbcnokFDrGfhSJHZ+/SkYsJ4zpvEF4ROpXiUdY2qTyqo8VEl47n4tFBJDjylzB30x OBUGump1fHSpMq71Vg0dfZ3Lnfc6Wt+CLYiCZPcvCzg70eit9z76rwOLUttzSY8FrTbP xKQI2HZN03dgZg9A7aa72OOH1Mxgvcem3hybl42Wlfv4xyHiBygKZAHzYrZqAEWm7K3f rRiA== X-Gm-Message-State: APjAAAUlJeipQS2r3aO7oZZPzLbOocs6+D6Dvd1Yl2rSvuRpE0vFD7hz pf03lyPBk9Ciofv/vm1FpTZ730TkHoAoG/o= X-Received: by 2002:a63:ee04:: with SMTP id e4mr7029591pgi.53.1567064778413; Thu, 29 Aug 2019 00:46:18 -0700 (PDT) Date: Thu, 29 Aug 2019 00:45:59 -0700 In-Reply-To: <20190829074603.70424-1-saravanak@google.com> Message-Id: <20190829074603.70424-4-saravanak@google.com> Mime-Version: 1.0 References: <20190829074603.70424-1-saravanak@google.com> X-Mailer: git-send-email 2.23.0.187.g17f5b7556c-goog Subject: [PATCH v10 3/7] 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 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 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 | 65 ++++++++++++++++++++++++++++++++++++++++++ include/linux/device.h | 24 ++++++++++++++++ 2 files changed, 89 insertions(+) diff --git a/drivers/base/core.c b/drivers/base/core.c index 39633bb75f0f..5e79d5820e79 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 supplier_sync_state_disabled; #ifdef CONFIG_SRCU static DEFINE_MUTEX(device_links_lock); @@ -648,6 +650,62 @@ 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(); + supplier_sync_state_disabled++; + device_links_write_unlock(); +} + +void device_links_supplier_sync_state_resume(void) +{ + struct device *dev, *tmp; + + device_links_write_lock(); + if (!supplier_sync_state_disabled) { + WARN(true, "Unmatched sync_state pause/resume!"); + goto out; + } + supplier_sync_state_disabled--; + if (supplier_sync_state_disabled) + 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 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 +750,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 (supplier_sync_state_disabled) + __device_links_supplier_defer_sync(link->supplier); + else + __device_links_supplier_sync_state(link->supplier); } dev->links.status = DL_DEV_DRIVER_BOUND; @@ -808,6 +871,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 +1846,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 76458cfbb267..7ab29bb3eb8c 100644 --- a/include/linux/device.h +++ b/include/linux/device.h @@ -80,6 +80,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. * @@ -123,6 +130,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); @@ -340,6 +348,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. @@ -379,6 +394,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); @@ -1132,12 +1148,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; }; @@ -1213,6 +1231,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. * @@ -1309,6 +1330,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) @@ -1651,6 +1673,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); #ifndef dev_fmt #define dev_fmt(fmt) fmt -- 2.23.0.187.g17f5b7556c-goog