driver core: Add state_synced sysfs file for devices that support it
authorSaravana Kannan <saravanak@google.com>
Thu, 21 May 2020 19:17:59 +0000 (12:17 -0700)
committerGreg Kroah-Hartman <gregkh@linuxfoundation.org>
Fri, 10 Jul 2020 13:24:56 +0000 (15:24 +0200)
This can be used to check if a device supports sync_state() callbacks
and therefore keeps resources left on by the bootloader enabled till all
its consumers have probed.

This can also be used to check if sync_state() has been called for a
device or whether it is still trying to keep resources enabled because
they were left enabled by the bootloader and all its consumers haven't
probed yet.

Signed-off-by: Saravana Kannan <saravanak@google.com>
Link: https://lore.kernel.org/r/20200521191800.136035-3-saravanak@google.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Documentation/ABI/testing/sysfs-devices-state_synced [new file with mode: 0644]
drivers/base/dd.c

diff --git a/Documentation/ABI/testing/sysfs-devices-state_synced b/Documentation/ABI/testing/sysfs-devices-state_synced
new file mode 100644 (file)
index 0000000..0c922d7
--- /dev/null
@@ -0,0 +1,24 @@
+What:          /sys/devices/.../state_synced
+Date:          May 2020
+Contact:       Saravana Kannan <saravanak@google.com>
+Description:
+               The /sys/devices/.../state_synced attribute is only present for
+               devices whose bus types or driver provides the .sync_state()
+               callback. The number read from it (0 or 1) reflects the value
+               of the device's 'state_synced' field. A value of 0 means the
+               .sync_state() callback hasn't been called yet. A value of 1
+               means the .sync_state() callback has been called.
+
+               Generally, if a device has sync_state() support and has some of
+               the resources it provides enabled at the time the kernel starts
+               (Eg: enabled by hardware reset or bootloader or anything that
+               run before the kernel starts), then it'll keep those resources
+               enabled and in a state that's compatible with the state they
+               were in at the start of the kernel. The device will stop doing
+               this only when the sync_state() callback has been called --
+               which happens only when all its consumer devices are registered
+               and have probed successfully. Resources that were left disabled
+               at the time the kernel starts are not affected or limited in
+               any way by sync_state() callbacks.
+
+
index 2aef1e4..a0b9ff5 100644 (file)
@@ -462,6 +462,18 @@ static void driver_deferred_probe_add_trigger(struct device *dev,
                driver_deferred_probe_trigger();
 }
 
+static ssize_t state_synced_show(struct device *dev,
+                                struct device_attribute *attr, char *buf)
+{
+       bool val;
+
+       device_lock(dev);
+       val = dev->state_synced;
+       device_unlock(dev);
+       return sprintf(buf, "%u\n", val);
+}
+static DEVICE_ATTR_RO(state_synced);
+
 static int really_probe(struct device *dev, struct device_driver *drv)
 {
        int ret = -EPROBE_DEFER;
@@ -535,9 +547,16 @@ re_probe:
                goto dev_groups_failed;
        }
 
+       if (dev_has_sync_state(dev) &&
+           device_create_file(dev, &dev_attr_state_synced)) {
+               dev_err(dev, "state_synced sysfs add failed\n");
+               goto dev_sysfs_state_synced_failed;
+       }
+
        if (test_remove) {
                test_remove = false;
 
+               device_remove_file(dev, &dev_attr_state_synced);
                device_remove_groups(dev, drv->dev_groups);
 
                if (dev->bus->remove)
@@ -567,6 +586,8 @@ re_probe:
                 drv->bus->name, __func__, dev_name(dev), drv->name);
        goto done;
 
+dev_sysfs_state_synced_failed:
+       device_remove_groups(dev, drv->dev_groups);
 dev_groups_failed:
        if (dev->bus->remove)
                dev->bus->remove(dev);
@@ -1104,6 +1125,7 @@ static void __device_release_driver(struct device *dev, struct device *parent)
 
                pm_runtime_put_sync(dev);
 
+               device_remove_file(dev, &dev_attr_state_synced);
                device_remove_groups(dev, drv->dev_groups);
 
                if (dev->bus && dev->bus->remove)