Linux driver-core infrastructure
 help / color / mirror / Atom feed
From: Pavol Sakac <sakacpav@amazon.de>
To: Greg Kroah-Hartman <gregkh@linuxfoundation.org>,
	Tejun Heo <tj@kernel.org>,
	"Rafael J . Wysocki" <rafael@kernel.org>,
	Danilo Krummrich <dakr@kernel.org>
Cc: <driver-core@lists.linux.dev>, <linux-kernel@vger.kernel.org>,
	"Andy Shevchenko" <andriy.shevchenko@linux.intel.com>,
	Xu Yang <xu.yang_2@nxp.com>,
	Bartosz Golaszewski <bartosz.golaszewski@oss.qualcomm.com>,
	Bjorn Helgaas <bhelgaas@google.com>, <linux-pci@vger.kernel.org>,
	Alex Williamson <alex@shazbot.org>, <kvm@vger.kernel.org>,
	<nh-open-source@amazon.com>
Subject: [RFC PATCH 3/8] sysfs: add opt-in staged directory creation and publication
Date: Fri, 11 Sep 2026 19:43:35 +0200	[thread overview]
Message-ID: <20260911174414.97060-3-sakacpav@amazon.de> (raw)
In-Reply-To: <20260911-vfopt-s5-v1-0-fa4cacdb6ca8@amazon.de>

Wire the kernfs staged mechanism into sysfs without touching a single
population entry point. struct kobject gains one opt-in bit, sd_staged,
in the existing state bitfield hole, so no struct grows. When it is set,
sysfs_create_dir_ns() builds the directory with
kernfs_create_dir_ns_staged(); because kobj->sd is valid throughout the
window, every existing population path runs verbatim and staging lives
entirely below them in kernfs. The kobj->sd store becomes
smp_store_release() for every kobject, eager paths included, ordering
the node's initialisation before the pointer's publication.

sysfs_publish_dir() performs the staged-to-visible transition via
kernfs_publish(). It is __must_check like its sibling directory
creators, because an ignored publication failure leaves a permanently
invisible directory. The only modular caller anticipated is the KUnit
suite a later patch in this series adds, so the export is scoped with
EXPORT_SYMBOL_IF_KUNIT().

Assisted-by: LLM
Signed-off-by: Pavol Sakac <sakacpav@amazon.de>
---
 fs/sysfs/dir.c          | 52 ++++++++++++++++++++++++++++++++++++++---
 include/linux/kobject.h | 25 ++++++++++++++++++++
 include/linux/sysfs.h   |  6 +++++
 3 files changed, 80 insertions(+), 3 deletions(-)

diff --git a/fs/sysfs/dir.c b/fs/sysfs/dir.c
index ffdcd4153c58..1694c01e8b84 100644
--- a/fs/sysfs/dir.c
+++ b/fs/sysfs/dir.c
@@ -11,6 +11,7 @@
 
 #define pr_fmt(fmt)	"sysfs: " fmt
 
+#include <kunit/visibility.h>
 #include <linux/fs.h>
 #include <linux/kobject.h>
 #include <linux/slab.h>
@@ -56,18 +57,63 @@ int sysfs_create_dir_ns(struct kobject *kobj, const struct ns_common *ns)
 
 	kobject_get_ownership(kobj, &uid, &gid);
 
-	kn = kernfs_create_dir_ns(parent, kobject_name(kobj), 0755, uid, gid,
-				  kobj, ns);
+	if (kobject_sd_staged(kobj))
+		kn = kernfs_create_dir_ns_staged(parent, kobject_name(kobj),
+						 0755, uid, gid, kobj, ns);
+	else
+		kn = kernfs_create_dir_ns(parent, kobject_name(kobj), 0755,
+					  uid, gid, kobj, ns);
 	if (IS_ERR(kn)) {
 		if (PTR_ERR(kn) == -EEXIST)
 			sysfs_warn_dup(parent, kobject_name(kobj));
 		return PTR_ERR(kn);
 	}
 
-	kobj->sd = kn;
+	/*
+	 * Publish the node with release semantics: every initialisation of
+	 * @kn above is ordered before the pointer store, so a task that
+	 * learns of @kobj through a synchronising handoff (registration
+	 * locks, a uevent, a notifier) observes a fully initialised node.
+	 * The kernfs staged funnel re-verifies the flags it routes on under
+	 * its mutex, so its pre-lock advisory reads decide nothing.
+	 */
+	smp_store_release(&kobj->sd, kn);
 	return 0;
 }
 
+/**
+ * sysfs_publish_dir - make a staged kobject directory visible
+ * @kobj: object whose staged directory is to be published
+ *
+ * Completes the staged creation begun by sysfs_create_dir_ns() when
+ * kobject_set_sd_staged() armed it: links the directory and everything
+ * populated beneath it into the parent and activates it in a single step.
+ * On a name collision the standard duplicate-name warning is emitted,
+ * matching sysfs_create_dir_ns().
+ *
+ * Return: 0 on success, -EEXIST on a name collision, -ENOENT if the parent
+ * went away, or -EINVAL on misuse (WARN).  On failure the subtree stays staged
+ * and is removed by the caller's normal error unwind
+ * (kobject_del()/sysfs_remove_dir()).
+ */
+int sysfs_publish_dir(struct kobject *kobj)
+{
+	int ret;
+
+	if (WARN_ON(!kobj || !kobj->sd))
+		return -EINVAL;
+
+	ret = kernfs_publish(kobj->sd);
+	if (ret == -EEXIST) {
+		struct kernfs_node *parent = kernfs_get_parent(kobj->sd);
+
+		sysfs_warn_dup(parent, kobject_name(kobj));
+		kernfs_put(parent);
+	}
+	return ret;
+}
+EXPORT_SYMBOL_IF_KUNIT(sysfs_publish_dir);
+
 /**
  *	sysfs_remove_dir - remove an object's directory.
  *	@kobj:	object.
diff --git a/include/linux/kobject.h b/include/linux/kobject.h
index 55e37a5d405e..938ff5ff747e 100644
--- a/include/linux/kobject.h
+++ b/include/linux/kobject.h
@@ -75,12 +75,37 @@ struct kobject {
 	unsigned int state_add_uevent_sent:1;
 	unsigned int state_remove_uevent_sent:1;
 	unsigned int uevent_suppress:1;
+	/* see kobject_set_sd_staged() */
+	unsigned int sd_staged:1;
 
 #ifdef CONFIG_DEBUG_KOBJECT_RELEASE
 	struct delayed_work	release;
 #endif
 };
 
+/**
+ * kobject_set_sd_staged - arm staged sysfs directory creation
+ * @kobj: object whose directory creation mode is being set
+ * @staged: true to create the directory staged (invisible)
+ *
+ * Set before kobject_add(); read by sysfs_create_dir_ns() at add time and
+ * by the caller's publish path afterwards.  The bit is the snapshot of
+ * what sysfs honored at add time; publish paths must read it, not
+ * whatever live state armed it, so the query cannot race the
+ * registration.  The bit is never cleared: a kobject re-added after
+ * kobject_del() is staged again and requires another sysfs_publish_dir().
+ */
+static inline void kobject_set_sd_staged(struct kobject *kobj, bool staged)
+{
+	kobj->sd_staged = staged;
+}
+
+/* see kobject_set_sd_staged() */
+static inline bool kobject_sd_staged(const struct kobject *kobj)
+{
+	return kobj->sd_staged;
+}
+
 __printf(2, 3) int kobject_set_name(struct kobject *kobj, const char *name, ...);
 __printf(2, 0) int kobject_set_name_vargs(struct kobject *kobj, const char *fmt, va_list vargs);
 
diff --git a/include/linux/sysfs.h b/include/linux/sysfs.h
index b1a3a1e6ad09..b537a5b52e1a 100644
--- a/include/linux/sysfs.h
+++ b/include/linux/sysfs.h
@@ -397,6 +397,7 @@ struct sysfs_ops {
 #ifdef CONFIG_SYSFS
 
 int __must_check sysfs_create_dir_ns(struct kobject *kobj, const struct ns_common *ns);
+int __must_check sysfs_publish_dir(struct kobject *kobj);
 void sysfs_remove_dir(struct kobject *kobj);
 int __must_check sysfs_rename_dir_ns(struct kobject *kobj, const char *new_name,
 				     const struct ns_common *new_ns);
@@ -507,6 +508,11 @@ static inline int sysfs_create_dir_ns(struct kobject *kobj, const struct ns_comm
 	return 0;
 }
 
+static inline int sysfs_publish_dir(struct kobject *kobj)
+{
+	return 0;
+}
+
 static inline void sysfs_remove_dir(struct kobject *kobj)
 {
 }
-- 
2.47.3


  parent reply	other threads:[~2026-09-11 17:45 UTC|newest]

Thread overview: 11+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2026-09-11 17:43 [RFC PATCH 0/8] kernfs, driver core: staged sysfs registration Pavol Sakac
2026-09-11 17:43 ` [RFC PATCH 1/8] kernfs: factor out reusable directory helpers Pavol Sakac
2026-09-11 17:43 ` [RFC PATCH 2/8] kernfs: add staged directory creation and publication Pavol Sakac
2026-09-11 17:43 ` Pavol Sakac [this message]
2026-09-11 17:43 ` [RFC PATCH 4/8] driver core: Register opted-in devices through the staged sysfs path Pavol Sakac
2026-09-11 17:43 ` [RFC PATCH 5/8] drivers: base: test: Add KUnit suite for staged sysfs registration Pavol Sakac
2026-09-12 13:14   ` Andy Shevchenko
2026-09-11 17:43 ` [RFC PATCH 6/8] driver core: Defer uevents for devices registered in a staged window Pavol Sakac
2026-09-12 13:20   ` Andy Shevchenko
2026-09-11 17:43 ` [RFC PATCH 7/8] PCI/IOV: Register virtual functions through the staged sysfs path Pavol Sakac
2026-09-11 17:43 ` [RFC PATCH 8/8] vfio: Opt the group and vfio-dev class devices into staged sysfs Pavol Sakac

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=20260911174414.97060-3-sakacpav@amazon.de \
    --to=sakacpav@amazon.de \
    --cc=alex@shazbot.org \
    --cc=andriy.shevchenko@linux.intel.com \
    --cc=bartosz.golaszewski@oss.qualcomm.com \
    --cc=bhelgaas@google.com \
    --cc=dakr@kernel.org \
    --cc=driver-core@lists.linux.dev \
    --cc=gregkh@linuxfoundation.org \
    --cc=kvm@vger.kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-pci@vger.kernel.org \
    --cc=nh-open-source@amazon.com \
    --cc=rafael@kernel.org \
    --cc=tj@kernel.org \
    --cc=xu.yang_2@nxp.com \
    /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