[RFC PATCH 04/14] driver core: add struct sysfs_lazy_state and device_set_sysfs_lazy()

Pavol Sakac <[email protected]>
Newsgroups org.infradead.lists.kexec,dev.linux.lists.driver-core,org.kernel.vger.linux-kernel,org.kvack.linux-mm
Message-ID <[email protected]>
Add the per-device lazy-sysfs bookkeeping struct and accessors that
subsystems use to opt their devices into lazy population:

  - struct sysfs_lazy_state { mutex lock; bool populated;
    bool power_added; } in include/linux/device.h.
  - dev->sysfs_lazy pointer field on struct device. Non-NULL is the
    opt-in signal device_add() consults to mark the kobj's kernfs
    directory KERNFS_LAZY.
  - Public accessors device_is_sysfs_lazy(), device_sysfs_populated(),
    device_sysfs_set_populated().
    device_sysfs_populated() uses smp_load_acquire() to pair with
    device_sysfs_set_populated()'s smp_store_release(); this lets
    the lockless fast-path in device_ktype_populate_all() (added in
    the next commit) skip the mutex while still observing all
    kernfs_create_*() side-effects performed by populate_all under
    @lock. The canonical write is under @lock.
  - device_set_sysfs_lazy() allocates the bookkeeping; idempotent and
    must be called BEFORE device_add(). Freed by device_release().

device_add() consults dev->sysfs_lazy after kobject_add(); if set,
kernfs_set_lazy(dev->kobj.sd) gates kernfs lookups and readdirs to
device_ktype.populate / populate_all.  No driver-core attribute
content is yet routed through the walker - that lands in the next
commit.  No existing caller opts in here; the plumbing is in place
for subsystems that follow in this series.

The "blob whose pointer doubles as the opt-in signal" pattern
mirrors dev_iommu_get() in drivers/iommu/iommu.c.

Cc: Greg Kroah-Hartman <[email protected]>
Cc: Rafael J. Wysocki <[email protected]>
Cc: Tejun Heo <[email protected]>
Cc: Danilo Krummrich <[email protected]>
Cc: [email protected]
Cc: [email protected]
Cc: [email protected]
Assisted-by: Claude:claude-opus-4.7
Signed-off-by: Pavol Sakac <[email protected]>
---
 drivers/base/core.c    | 74 ++++++++++++++++++++++++++++++++++++++++++
 include/linux/device.h | 28 ++++++++++++++++
 2 files changed, 102 insertions(+)

diff --git a/drivers/base/core.c b/drivers/base/core.c
index bd2ddf2aab505..6d0d917d4b1ff 100644
--- a/drivers/base/core.c
+++ b/drivers/base/core.c
@@ -2555,6 +2555,10 @@ static void device_release(struct kobject *kobj)
 	 */
 	devres_release_all(dev);
 
+	if (dev->sysfs_lazy)
+		mutex_destroy(&dev->sysfs_lazy->lock);
+	kfree(dev->sysfs_lazy);
+
 	kfree(dev->dma_range_map);
 	kfree(dev->driver_override.name);
 
@@ -2588,6 +2592,67 @@ static void device_get_ownership(const struct kobject *kobj, kuid_t *uid, kgid_t
 		dev->class->get_ownership(dev, uid, gid);
 }
 
+/*
+ * True once populate_all has completed; lockless.  False for non-lazy.
+ *
+ * Memory-ordering: pairs with device_sysfs_set_populated()'s
+ * smp_store_release().  smp_load_acquire ensures any kernfs_create_*()
+ * side-effects performed by populate_all under sysfs_lazy.lock become
+ * visible to readers that observe @populated == true.  The kernfs
+ * lookup path also takes kernfs_rwsem(read) which provides an
+ * independent memory-barrier; the explicit acquire here is belt-and-
+ * braces for callers that may bypass kernfs (e.g. internal sysfs_*
+ * helpers and the KUnit suite).
+ */
+bool device_sysfs_populated(const struct device *dev)
+{
+	return dev->sysfs_lazy &&
+	       smp_load_acquire(&dev->sysfs_lazy->populated);
+}
+
+/*
+ * Latch @dev as fully populated. Caller holds @dev->sysfs_lazy->lock.
+ *
+ * Memory-ordering: smp_store_release() publishes all kernfs_create_*()
+ * side-effects performed under sysfs_lazy.lock to lockless readers
+ * via device_sysfs_populated()'s smp_load_acquire().
+ */
+void device_sysfs_set_populated(struct device *dev)
+{
+	smp_store_release(&dev->sysfs_lazy->populated, true);
+}
+
+/**
+ * device_set_sysfs_lazy - opt @dev into lazy sysfs (call before device_add())
+ * @dev: device to opt in
+ *
+ * Allocates @dev->sysfs_lazy.  Idempotent: a second call on a
+ * device that already opted in is a no-op.  Must be called before
+ * device_add().  After device_add() returns, eager attribute creation
+ * is skipped and walker dispatch lazily materializes attributes on
+ * first sysfs lookup or readdir.
+ *
+ * Return:
+ * * %0       - on success (or if already opted in).
+ * * %-ENOMEM - allocation failure.
+ */
+int device_set_sysfs_lazy(struct device *dev)
+{
+	struct sysfs_lazy_state *lazy;
+
+	if (dev->sysfs_lazy)
+		return 0;
+
+	lazy = kzalloc_obj(*lazy);
+	if (!lazy)
+		return -ENOMEM;
+
+	mutex_init(&lazy->lock);
+	dev->sysfs_lazy = lazy;
+	return 0;
+}
+EXPORT_SYMBOL_GPL(device_set_sysfs_lazy);
+
 static const struct kobj_type device_ktype = {
 	.release	= device_release,
 	.sysfs_ops	= &dev_sysfs_ops,
@@ -3642,6 +3707,15 @@ int device_add(struct device *dev)
 	error = device_add_class_symlinks(dev);
 	if (error)
 		goto SymlinkError;
+	/*
+	 * device_set_sysfs_lazy() only allocates ->sysfs_lazy_state for
+	 * non-namespaced devices, and device_is_sysfs_lazy() guards opt-in
+	 * here, so kernfs_set_lazy() should always succeed.  WARN_ON catches
+	 * a regression in those preconditions.
+	 */
+	if (device_is_sysfs_lazy(dev))
+		WARN_ON(kernfs_set_lazy(dev->kobj.sd));
+
 	error = device_add_attrs(dev);
 	if (error)
 		goto AttrsError;
diff --git a/include/linux/device.h b/include/linux/device.h
index 9c8fde6a3d866..39e08e8c950c6 100644
--- a/include/linux/device.h
+++ b/include/linux/device.h
@@ -45,6 +45,18 @@ struct fwnode_handle;
 struct iommu_group;
 struct dev_pin_info;
 struct dev_iommu;
+/**
+ * struct sysfs_lazy_state - per-device lazy sysfs population state
+ * @lock: serialises populate callbacks (held across device_sysfs_apply())
+ * @populated: full-populate latch
+ * @power_added: dpm_sysfs_add() latch
+ */
+struct sysfs_lazy_state {
+	struct mutex	lock;
+	bool		populated;
+	bool		power_added;
+};
+
 struct msi_device_data;
 
 /**
@@ -739,9 +751,25 @@ struct device {
 	bool			dma_iommu:1;
 #endif
 
+	/* Lazy-sysfs opt-in (NULL = eager). Set via device_set_sysfs_lazy(). */
+	struct sysfs_lazy_state	*sysfs_lazy;
+
 	DECLARE_BITMAP(flags, DEV_FLAG_COUNT);
 };
 
+/**
+ * device_is_sysfs_lazy - true if @dev opted into lazy sysfs
+ * @dev: device to query
+ */
+static inline bool device_is_sysfs_lazy(const struct device *dev)
+{
+	return !!dev->sysfs_lazy;
+}
+
+bool device_sysfs_populated(const struct device *dev);
+void device_sysfs_set_populated(struct device *dev);
+int device_set_sysfs_lazy(struct device *dev);
+
 #define __create_dev_flag_accessors(accessor_name, flag_name) \
 static inline bool dev_##accessor_name(const struct device *dev) \
 { \
-- 
2.47.3




Amazon Web Services Development Center Germany GmbH
Tamara-Danz-Str. 13
10243 Berlin
Geschaeftsfuehrung: Christof Hellmis, Andreas Stieger
Eingetragen am Amtsgericht Charlottenburg unter HRB 257764 B
Sitz: Berlin
Ust-ID: DE 365 538 597
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.