From: Pavol Sakac <sakacpav@amazon.de>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
"Rafael J . Wysocki" <rafael@kernel.org>,
Danilo Krummrich <dakr@kernel.org>, Tejun Heo <tj@kernel.org>
Cc: <linux-kernel@vger.kernel.org>, <driver-core@lists.linux.dev>,
"David Woodhouse" <dwmw@amazon.co.uk>,
Pasha Tatashin <pasha.tatashin@soleen.com>,
Mike Rapoport <rppt@kernel.org>,
Pratyush Yadav <pratyush@kernel.org>,
"David Matlack" <dmatlack@google.com>,
Samiullah Khawaja <skhawaja@google.com>,
Alexander Graf <graf@amazon.com>, <linux-mm@kvack.org>,
<kexec@lists.infradead.org>, <nh-open-source@amazon.com>
Subject: [RFC PATCH 13/14] Documentation: add lazy sysfs initialisation and device_sysfs_entry docs
Date: Thu, 2 Jul 2026 19:51:13 +0200 [thread overview]
Message-ID: <20260702175114.24659-9-sakacpav@amazon.de> (raw)
In-Reply-To: <20260702175114.24659-1-sakacpav@amazon.de>
Document the lazy sysfs mechanism and its declarative walker:
- Documentation/driver-api/sysfs-lazy.rst: developer guide
covering overview, opting-in, populate callbacks (per-device
walker and the iommu_group small-table pattern), locking,
teardown, known limitations, and a "See also" section
pointing to the KUnit tests and the kselftest.
- Documentation/ABI/testing/sysfs-lazy: userspace-visible
semantics - lookup vs readdir trigger points, stat() behaviour
on known and unknown names, inotify / fanotify interaction,
udev cold-plug compatibility, and the list of currently
opted-in device classes (SR-IOV VFs, VFIO, iommu_groups).
- Documentation/driver-api/index.rst: add sysfs-lazy to the
toctree.
- MAINTAINERS: add F: entries for the new docs, the KUnit test
drivers/base/test/device_sysfs_apply_test.c, and the
tools/testing/selftests/sysfs-lazy/ directory (added in the
next commit) under DRIVER CORE, KOBJECTS, DEBUGFS AND SYSFS.
Cc: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
Cc: Rafael J. Wysocki <rafael@kernel.org>
Cc: Jonathan Corbet <corbet@lwn.net>
Cc: linux-doc@vger.kernel.org
Cc: linux-kernel@vger.kernel.org
Cc: linux-api@vger.kernel.org
Cc: Danilo Krummrich <dakr@kernel.org>
Cc: driver-core@lists.linux.dev
Cc: Shuah Khan <shuah@kernel.org>
Cc: linux-kselftest@vger.kernel.org
Assisted-by: Claude:claude-opus-4.7
Signed-off-by: Pavol Sakac <sakacpav@amazon.de>
---
Documentation/ABI/testing/sysfs-lazy | 77 +++++++++++++
Documentation/driver-api/index.rst | 1 +
Documentation/driver-api/sysfs-lazy.rst | 146 ++++++++++++++++++++++++
MAINTAINERS | 4 +
4 files changed, 228 insertions(+)
create mode 100644 Documentation/ABI/testing/sysfs-lazy
create mode 100644 Documentation/driver-api/sysfs-lazy.rst
diff --git a/Documentation/ABI/testing/sysfs-lazy b/Documentation/ABI/testing/sysfs-lazy
new file mode 100644
index 0000000000000..3a1b2a0e97e1c
--- /dev/null
+++ b/Documentation/ABI/testing/sysfs-lazy
@@ -0,0 +1,77 @@
+What: /sys/devices/.../
+ /sys/kernel/iommu_groups/<N>/
+Date: May 2026
+KernelVersion: 7.1
+Contact: Pavol Sakac <sakacpav@amazon.de>
+Description:
+ This entry documents the *realisation timing* contract
+ for sysfs entries under devices that have opted into
+ lazy population. All attribute names, contents, modes,
+ and ownership are identical to the eager-device tree;
+ only the moment of creation differs. The opt-in set is
+ enumerated in "Currently opted-in device classes" below.
+
+ When a device driver sets the sysfs_lazy flag on a struct
+ device before calling device_add(), the kernel defers
+ creation of attribute files, attribute groups, and
+ class/bus/driver symlinks under that device's sysfs
+ directory.
+
+ These entries are materialised on demand:
+
+ - A lookup (open, stat, readlink) of a specific filename
+ triggers creation of that single file or symlink via
+ the ktype->populate callback, which drives
+ device_sysfs_apply() with DEV_SYSFS_ADD_ONE.
+ - A directory listing (readdir / getdents) triggers
+ creation of all deferred entries at once via the
+ ktype->populate_all callback, which drives
+ device_sysfs_apply() with DEV_SYSFS_ADD_ALL.
+
+ A stat() call on a not-yet-realised filename:
+
+ - If the name corresponds to a known row (including one
+ owned by dev->type->entries), the attribute or
+ symlink is created and stat() succeeds with the same
+ metadata an eager device would show.
+ - If the name is unknown, stat() returns -ENOENT and
+ kernfs caches a negative dentry. Transient errors
+ (-ENOMEM, -EBUSY) propagate unchanged and are NOT
+ negative-cached; the next lookup retries.
+ access(F_OK) behaves identically to stat().
+
+ readdir() returns the full set of names the device
+ would expose if eager - applies_to gating is evaluated
+ during populate_all(), so the visible set exactly
+ matches the eager-device tree.
+
+ Once a row's entry is realised, it persists for the
+ lifetime of the device. A second open()/stat() is a
+ plain kernfs lookup and involves no walker invocation.
+
+ What does NOT change relative to an eager device:
+
+ - The set of files and their names (applies_to is the
+ single gate, identical between eager and lazy paths).
+ - File contents, permissions, ownership, and SELinux
+ labels.
+ - The device directory itself (always eager) and its
+ parent chain.
+
+ Interaction with inotify / fanotify:
+ Watchers see IN_CREATE events at first-access time,
+ not at device_add() time.
+
+ Interaction with udev / systemd-udevd:
+ udev cold-plug works without modification. udev
+ reads the device directory on KOBJ_ADD, which
+ triggers readdir -> full realisation.
+
+ Currently opted-in device classes:
+
+ - PCI SR-IOV Virtual Functions (drivers/pci/iov.c)
+ - VFIO group and vfio-dev children (drivers/vfio/)
+ - IOMMU groups (/sys/kernel/iommu_groups/<N>/,
+ drivers/iommu/iommu.c)
+
+Users: udev, systemd-udevd, libvirt, DPDK, SPDK
diff --git a/Documentation/driver-api/index.rst b/Documentation/driver-api/index.rst
index eaf7161ff9578..b1807ca6cd1ce 100644
--- a/Documentation/driver-api/index.rst
+++ b/Documentation/driver-api/index.rst
@@ -23,6 +23,7 @@ of interest to most developers working on device drivers.
driver-model/index
device_link
infrastructure
+ sysfs-lazy
ioctl
pm/index
diff --git a/Documentation/driver-api/sysfs-lazy.rst b/Documentation/driver-api/sysfs-lazy.rst
new file mode 100644
index 0000000000000..5fbd7732afe0d
--- /dev/null
+++ b/Documentation/driver-api/sysfs-lazy.rst
@@ -0,0 +1,146 @@
+.. SPDX-License-Identifier: GPL-2.0
+
+=========================
+Lazy sysfs initialisation
+=========================
+
+Overview
+========
+
+``device_add()`` normally creates every sysfs attribute, attribute
+group, and class/bus/driver symlink under a new device's directory
+synchronously. For populations created in bulk - hundreds of PCI
+SR-IOV VFs, fleets of vfio-dev children - that work dominates
+``device_add()`` latency yet the resulting sysfs nodes are rarely
+read.
+
+Lazy sysfs defers attribute creation until the directory is first
+opened by userspace. The device directory itself, its parent chain,
+and uevent broadcast remain eager; only the *content* of the
+directory is deferred.
+
+Opting in
+=========
+
+A subsystem opts a device in by:
+
+1. Calling ``device_set_sysfs_lazy(dev)`` before ``device_add()``.
+2. Making the kernfs directory created by ``device_add()`` a lazy
+ directory via ``kernfs_set_lazy()``.
+
+``kernfs_set_lazy()`` flips ``KERNFS_LAZY`` on a directory under
+``kernfs_rwsem`` and returns ``-EINVAL`` for namespaced nodes (a
+non-NULL namespace tag or the ``KERNFS_NS`` flag) and non-directory
+nodes: lazy dispatch does not carry an active namespace tag into the
+populate callback.
+
+The flag is set once and never cleared.
+
+Populate callbacks
+==================
+
+A lazy directory's first ``readdir(3)`` or ``open(2)`` of a
+not-yet-created child invokes one of two callbacks defined on
+``struct kobj_type``::
+
+ int (*populate)(struct kobject *kobj, const char *name);
+ void (*populate_all)(struct kobject *kobj);
+
+``populate(name)`` runs on a named lookup miss and creates the single
+matching attribute. ``populate_all()`` runs on the first
+``readdir(3)`` and creates every attribute the device exposes.
+
+For a ``struct device``, the driver core wires both callbacks to
+``device_sysfs_apply()``, which walks an immutable
+``device_sysfs_entry`` table. Each row declares an
+``applies_to(dev)`` predicate, a ``create(dev, name)`` action, and a
+``remove(dev)`` action. The walker dispatches the rows whose
+predicate passes for ``@dev``; the rows themselves call
+``sysfs_create_file()``, ``sysfs_create_link()``, or
+``sysfs_create_group()`` directly.
+
+The same pattern is used by ``iommu_group``: a lazy ``iommu_group``
+kobject's ``populate``/``populate_all`` walks a small fixed table of
+group attributes (``reserved_regions``, ``type``).
+
+Locking
+=======
+
+Each lazy device holds ``dev->sysfs_lazy->lock`` (a mutex). All
+three populate paths take it::
+
+ populate(name) -> mutex_lock(lock)
+ walk(ADD_ONE, name)
+ mutex_unlock
+ populate_all() -> if (test_bit(POPULATED)) return;
+ mutex_lock(lock)
+ if (test_bit(POPULATED)) goto out;
+ if (dev->p->dead) goto out;
+ walk(ADD_ALL, NULL)
+ set_bit(POPULATED)
+ mutex_unlock
+ REMOVE_ALL teardown -> mutex_lock(lock)
+ walk(REMOVE_ALL, NULL)
+ mutex_unlock
+
+The ``dev->sysfs_lazy->populated`` bool is the
+``populate_all()`` once-latch. The unlocked read fast path is a
+performance optimisation; the field is only authoritative under
+``lock``. A stale false on the fast path harmlessly retakes
+the mutex and re-checks.
+
+Lock ordering: ``lock`` nests inside any caller-held VFS or
+kernfs lock that brought us into ``populate``/``populate_all``. No
+sysfs / kernfs operation that requires ``kernfs_rwsem`` write may be
+issued while holding ``lock``.
+
+The ``iommu_group`` path uses the same pattern:
+``group->sysfs_lazy.lock`` and
+``group->sysfs_lazy.populated``.
+
+Teardown
+========
+
+``device_del()`` sets ``dev->p->dead`` *before* the
+``REMOVE_ALL`` walker call and takes ``lock`` around it.
+Any populate callback racing with ``device_del()``:
+
+1. Either takes the lock first and runs against a not-yet-dead
+ device (its created files are torn down a moment later by
+ ``REMOVE_ALL``, which is idempotent against the row's
+ ``remove()``); or
+2. Sees ``dev->p->dead == true`` after acquiring the lock and bails
+ without creating anything.
+
+Either way, the device exits with a quiesced sysfs subtree and no
+populate callback sees a half-deleted device.
+
+``iommu_group_release()`` follows the same dead-flag + lock-then-walk
+contract.
+
+Known limitations
+=================
+
+* Lazy sysfs is only available for devices whose populate path is
+ driven through ``device_sysfs_entry`` rows. Subsystems with bespoke
+ ``device_add()``-time sysfs creation must migrate before opting in.
+* Namespaced kobjects cannot be marked lazy: ``kernfs_set_lazy()``
+ rejects them because the populate callback does not carry the
+ active namespace tag.
+* The directory's eager skeleton (the directory inode itself and the
+ ``uevent`` file emitted by ``device_add()``) is not deferred.
+* Per-attribute creation cost is unchanged. Lazy sysfs amortises
+ the cost across first-access events instead of paying it during
+ ``device_add()``.
+
+See also
+========
+
+* ``include/linux/kernfs.h`` - ``kernfs_set_lazy()``, ``KERNFS_LAZY``.
+* ``drivers/base/core.c`` - ``device_sysfs_apply()``,
+ ``driver_core_sysfs_entries[]``.
+* ``drivers/iommu/iommu.c`` - ``iommu_group_lazy_attrs[]``.
+* ``drivers/base/test/device_sysfs_apply_test.c`` - KUnit walker
+ contract tests.
+* ``tools/testing/selftests/sysfs-lazy/`` - userspace VFS-level
+ parity tests.
diff --git a/MAINTAINERS b/MAINTAINERS
index 5d8a887c868ed..1424e641d535f 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -7800,9 +7800,12 @@ M: Danilo Krummrich <dakr@kernel.org>
L: driver-core@lists.linux.dev
S: Supported
T: git git://git.kernel.org/pub/scm/linux/kernel/git/driver-core/driver-core.git
+F: Documentation/ABI/testing/sysfs-lazy
F: Documentation/core-api/kobject.rst
F: Documentation/driver-api/driver-model/
+F: Documentation/driver-api/sysfs-lazy.rst
F: drivers/base/
+F: drivers/base/test/device_sysfs_apply_test.c
F: fs/debugfs/
F: fs/sysfs/
F: include/linux/device/
@@ -7830,6 +7833,7 @@ F: samples/rust/rust_debugfs_scoped.rs
F: samples/rust/rust_driver_platform.rs
F: samples/rust/rust_driver_faux.rs
F: samples/rust/rust_soc.rs
+F: tools/testing/selftests/sysfs-lazy/
DRIVERS FOR OMAP ADAPTIVE VOLTAGE SCALING (AVS)
M: Nishanth Menon <nm@ti.com>
--
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
next prev parent reply other threads:[~2026-07-03 1:01 UTC|newest]
Thread overview: 17+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-07-02 17:40 [RFC PATCH 00/14] driver core: defer per-VF sysfs creation for fast SR-IOV bring-up Pavol Sakac
2026-07-02 17:40 ` [RFC PATCH 01/14] kernfs: add populate callbacks and KERNFS_LAZY flag for lazy dir population Pavol Sakac
2026-07-02 17:40 ` [RFC PATCH 02/14] sysfs: add existence-check helpers for lazy populate races Pavol Sakac
2026-07-02 17:40 ` [RFC PATCH 03/14] sysfs: introduce sysfs_kf_syscall_ops dispatching to kobj_type Pavol Sakac
2026-07-02 17:40 ` [RFC PATCH 04/14] driver core: add struct sysfs_lazy_state and device_set_sysfs_lazy() Pavol Sakac
2026-07-02 17:51 ` [RFC PATCH 05/14] driver core: add struct device_sysfs_entry and walker Pavol Sakac
2026-07-02 17:51 ` [RFC PATCH 06/14] driver core: wire device_ktype populate to walker Pavol Sakac
2026-07-02 17:51 ` [RFC PATCH 07/14] driver core: migrate device sysfs to device_sysfs_entry table Pavol Sakac
2026-07-02 17:51 ` [RFC PATCH 08/14] PCI/sysfs: migrate to device_sysfs_entry, defer physfn symlink Pavol Sakac
2026-07-02 17:51 ` [RFC PATCH 09/14] iommu: lazy-populate iommu_group reserved_regions/type attrs Pavol Sakac
2026-07-10 14:19 ` Greg Kroah-Hartman
2026-07-02 17:51 ` [RFC PATCH 10/14] PCI/IOV: opt SR-IOV VFs into sysfs_lazy Pavol Sakac
2026-07-02 17:51 ` [RFC PATCH 11/14] vfio: opt vfio-dev and VFIO group devices " Pavol Sakac
2026-07-02 17:51 ` [RFC PATCH 12/14] driver core: test: add KUnit tests for device_sysfs_apply Pavol Sakac
2026-07-02 17:51 ` Pavol Sakac [this message]
2026-07-02 17:51 ` [RFC PATCH 14/14] selftests: sysfs-lazy: add tests for lazy sysfs initialization Pavol Sakac
2026-07-10 14:16 ` [RFC PATCH 00/14] driver core: defer per-VF sysfs creation for fast SR-IOV bring-up Greg Kroah-Hartman
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260702175114.24659-9-sakacpav@amazon.de \
--to=sakacpav@amazon.de \
--cc=dakr@kernel.org \
--cc=dmatlack@google.com \
--cc=driver-core@lists.linux.dev \
--cc=dwmw@amazon.co.uk \
--cc=graf@amazon.com \
--cc=gregkh@linuxfoundation.org \
--cc=kexec@lists.infradead.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-mm@kvack.org \
--cc=nh-open-source@amazon.com \
--cc=pasha.tatashin@soleen.com \
--cc=pratyush@kernel.org \
--cc=rafael@kernel.org \
--cc=rppt@kernel.org \
--cc=skhawaja@google.com \
--cc=tj@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox