* [PATCH 1/4] fs: send uevents for filesystem mount events
2025-12-18 2:02 [PATCHSET V4 2/2] fs: send uevents on mount and unmount Darrick J. Wong
@ 2025-12-18 2:04 ` Darrick J. Wong
2025-12-18 5:26 ` Christoph Hellwig
` (2 more replies)
2025-12-18 2:04 ` [PATCH 2/4] xfs: send uevents when major filesystem events happen Darrick J. Wong
` (2 subsequent siblings)
3 siblings, 3 replies; 15+ messages in thread
From: Darrick J. Wong @ 2025-12-18 2:04 UTC (permalink / raw)
To: brauner, djwong; +Cc: linux-ext4, linux-xfs, hch, linux-fsdevel
From: Darrick J. Wong <djwong@kernel.org>
Add the ability to send uevents whenever a filesystem mounts, unmounts,
or goes down. This will enable XFS to start daemons whenever a
filesystem is first mounted.
Regrettably, we can't wire this directly into get_tree_bdev_flags or
generic_shutdown_super because not all filesystems set up a kobject
representation in sysfs, and the VFS has no idea if a filesystem
actually does that.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
include/linux/fsevent.h | 33 ++++++++++++
fs/Makefile | 2 -
fs/fsevent.c | 128 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 162 insertions(+), 1 deletion(-)
create mode 100644 include/linux/fsevent.h
create mode 100644 fs/fsevent.c
diff --git a/include/linux/fsevent.h b/include/linux/fsevent.h
new file mode 100644
index 00000000000000..548e35861e545f
--- /dev/null
+++ b/include/linux/fsevent.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2025 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef _LINUX_FSEVENT_H__
+#define _LINUX_FSEVENT_H__
+
+void fsevent_send_mount(struct super_block *sb, struct kobject *kobject,
+ struct fs_context *fc);
+
+void fsevent_send(struct super_block *sb, struct kobject *kobject,
+ enum kobject_action kaction);
+
+static inline void fsevent_send_unmount(struct super_block *sb,
+ struct kobject *kobject)
+{
+ fsevent_send(sb, kobject, KOBJ_REMOVE);
+}
+
+static inline void fsevent_send_remount(struct super_block *sb,
+ struct kobject *kobject)
+{
+ fsevent_send(sb, kobject, KOBJ_CHANGE);
+}
+
+static inline void fsevent_send_shutdown(struct super_block *sb,
+ struct kobject *kobject)
+{
+ fsevent_send(sb, kobject, KOBJ_OFFLINE);
+}
+
+#endif /* _LINUX_FSEVENT_H__ */
diff --git a/fs/Makefile b/fs/Makefile
index f238cc5ea2e9d7..4a07ae61e65730 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -16,7 +16,7 @@ obj-y := open.o read_write.o file_table.o super.o \
stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \
fs_dirent.o fs_context.o fs_parser.o fsopen.o init.o \
kernel_read_file.o mnt_idmapping.o remap_range.o pidfs.o \
- file_attr.o fserror.o
+ file_attr.o fserror.o fsevent.o
obj-$(CONFIG_BUFFER_HEAD) += buffer.o mpage.o
obj-$(CONFIG_PROC_FS) += proc_namespace.o
diff --git a/fs/fsevent.c b/fs/fsevent.c
new file mode 100644
index 00000000000000..a7eea8eac0578a
--- /dev/null
+++ b/fs/fsevent.c
@@ -0,0 +1,128 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2025 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include <linux/fs.h>
+#include <linux/kobject.h>
+#include <linux/fs_context.h>
+#include <linux/fsevent.h>
+
+static inline size_t fs_uevent_bufsize(const struct super_block *sb,
+ const char *source,
+ unsigned int *envlen)
+{
+ size_t ret = sizeof("TYPE=filesystem") +
+ sizeof("SID=") + sizeof_field(struct super_block, s_id);
+ *envlen += 2;
+
+ if (source) {
+ ret += sizeof("SOURCE=") + strlen(source) + 1;
+ (*envlen)++;
+ }
+
+ if (sb->s_uuid_len == sizeof(sb->s_uuid)) {
+ ret += sizeof("UUID=") + UUID_STRING_LEN;
+ (*envlen)++;
+ }
+
+ /* null array element terminator */
+ (*envlen)++;
+ return ret;
+}
+
+#define ADVANCE_ENV(envp, buf, buflen, written) \
+ do { \
+ ssize_t __written = (written); \
+\
+ WARN_ON((buflen) < (__written) + 1); \
+ *(envp) = (buf); \
+ (envp)++; \
+ (buf) += (__written) + 1; \
+ (buflen) -= (__written) + 1; \
+ } while (0)
+
+static char **format_uevent_strings(struct super_block *sb, const char *source)
+{
+ unsigned int envlen = 0;
+ size_t buflen = fs_uevent_bufsize(sb, source, &envlen);
+ char *buf;
+ char **env, **envp;
+ ssize_t written;
+
+ buf = kzalloc(buflen, GFP_KERNEL);
+ if (!buf)
+ return NULL;
+ env = kcalloc(envlen, sizeof(char *), GFP_KERNEL);
+ if (!env) {
+ kfree(buf);
+ return NULL;
+ }
+
+ envp = env;
+ written = snprintf(buf, buflen, "TYPE=filesystem");
+ if (written >= buflen)
+ goto bad;
+ ADVANCE_ENV(envp, buf, buflen, written);
+
+ written = snprintf(buf, buflen, "SID=%s", sb->s_id);
+ if (written >= buflen)
+ goto bad;
+ ADVANCE_ENV(envp, buf, buflen, written);
+
+ if (source) {
+ written = snprintf(buf, buflen, "SOURCE=%s", source);
+ if (written >= buflen)
+ goto bad;
+ ADVANCE_ENV(envp, buf, buflen, written);
+ }
+
+ if (sb->s_uuid_len == sizeof(sb->s_uuid)) {
+ written = snprintf(buf, buflen, "UUID=%pU", &sb->s_uuid);
+ if (written >= buflen)
+ goto bad;
+ ADVANCE_ENV(envp, buf, buflen, written);
+ }
+
+ return env;
+bad:
+ kfree(env);
+ kfree(buf);
+ return NULL;
+}
+
+static inline void free_uevent_strings(char **env)
+{
+ kfree(env[0]);
+ kfree(env);
+}
+
+/*
+ * Send a uevent signalling that the mount succeeded so we can use udev rules
+ * to start background services.
+ */
+void fsevent_send_mount(struct super_block *sb, struct kobject *kobject,
+ struct fs_context *fc)
+{
+ char **env = format_uevent_strings(sb, fc->source);
+
+ if (env) {
+ kobject_uevent_env(kobject, KOBJ_ADD, env);
+ free_uevent_strings(env);
+ }
+}
+EXPORT_SYMBOL_GPL(fsevent_send_mount);
+
+/* Send a uevent signalling that something happened to a live mount. */
+void fsevent_send(struct super_block *sb, struct kobject *kobject,
+ enum kobject_action kaction)
+{
+ char **env;
+
+ env = format_uevent_strings(sb, NULL);
+ if (env) {
+ kobject_uevent_env(kobject, kaction, env);
+ free_uevent_strings(env);
+ }
+}
+EXPORT_SYMBOL_GPL(fsevent_send);
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH 1/4] fs: send uevents for filesystem mount events
2025-12-18 2:04 ` [PATCH 1/4] fs: send uevents for filesystem mount events Darrick J. Wong
@ 2025-12-18 5:26 ` Christoph Hellwig
2025-12-18 19:49 ` Darrick J. Wong
2025-12-18 23:33 ` [PATCH V4.1 " Darrick J. Wong
2025-12-24 12:47 ` [PATCH " Christian Brauner
2 siblings, 1 reply; 15+ messages in thread
From: Christoph Hellwig @ 2025-12-18 5:26 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: brauner, linux-ext4, linux-xfs, linux-fsdevel
> +#define ADVANCE_ENV(envp, buf, buflen, written) \
> + do { \
> + ssize_t __written = (written); \
> +\
> + WARN_ON((buflen) < (__written) + 1); \
> + *(envp) = (buf); \
> + (envp)++; \
> + (buf) += (__written) + 1; \
> + (buflen) -= (__written) + 1; \
> + } while (0)
Any reason this is a macro vs an (inline?) function? Looking at this a
bit more, could this simply use a seq_buf?
^ permalink raw reply [flat|nested] 15+ messages in thread* Re: [PATCH 1/4] fs: send uevents for filesystem mount events
2025-12-18 5:26 ` Christoph Hellwig
@ 2025-12-18 19:49 ` Darrick J. Wong
0 siblings, 0 replies; 15+ messages in thread
From: Darrick J. Wong @ 2025-12-18 19:49 UTC (permalink / raw)
To: Christoph Hellwig; +Cc: brauner, linux-ext4, linux-xfs, linux-fsdevel
On Wed, Dec 17, 2025 at 09:26:41PM -0800, Christoph Hellwig wrote:
> > +#define ADVANCE_ENV(envp, buf, buflen, written) \
> > + do { \
> > + ssize_t __written = (written); \
> > +\
> > + WARN_ON((buflen) < (__written) + 1); \
> > + *(envp) = (buf); \
> > + (envp)++; \
> > + (buf) += (__written) + 1; \
> > + (buflen) -= (__written) + 1; \
> > + } while (0)
>
> Any reason this is a macro vs an (inline?) function? Looking at this a
> bit more, could this simply use a seq_buf?
I'll change it to something like this:
seq_buf_init(&sbuf, buf, buflen);
envp = env;
/*
* Add a second null terminator on the end so the next printf can start
* printing at the second null terminator.
*/
seq_buf_get_buf(&sbuf, envp++);
seq_buf_printf(&sbuf, "TYPE=filesystem");
seq_buf_putc(&sbuf, 0);
seq_buf_get_buf(&sbuf, envp++);
seq_buf_printf(&sbuf, "SID=%s", sb->s_id);
seq_buf_putc(&sbuf, 0);
...
/* Add null terminator to strings array */
*envp = NULL;
That does look a lot cleaner than opencoding it.
--D
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH V4.1 1/4] fs: send uevents for filesystem mount events
2025-12-18 2:04 ` [PATCH 1/4] fs: send uevents for filesystem mount events Darrick J. Wong
2025-12-18 5:26 ` Christoph Hellwig
@ 2025-12-18 23:33 ` Darrick J. Wong
2025-12-24 12:47 ` [PATCH " Christian Brauner
2 siblings, 0 replies; 15+ messages in thread
From: Darrick J. Wong @ 2025-12-18 23:33 UTC (permalink / raw)
To: brauner; +Cc: linux-ext4, linux-xfs, hch, linux-fsdevel
From: Darrick J. Wong <djwong@kernel.org>
Add the ability to send uevents whenever a filesystem mounts, unmounts,
or goes down. This will enable XFS to start daemons whenever a
filesystem is first mounted.
Regrettably, we can't wire this directly into get_tree_bdev_flags or
generic_shutdown_super because not all filesystems set up a kobject
representation in sysfs, and the VFS has no idea if a filesystem
actually does that.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
v4.1: replace open-coded buffer formatting with seqbuf
---
include/linux/fsevent.h | 33 ++++++++++++
fs/Makefile | 2 -
fs/fsevent.c | 126 +++++++++++++++++++++++++++++++++++++++++++++++
3 files changed, 160 insertions(+), 1 deletion(-)
create mode 100644 include/linux/fsevent.h
create mode 100644 fs/fsevent.c
diff --git a/include/linux/fsevent.h b/include/linux/fsevent.h
new file mode 100644
index 00000000000000..548e35861e545f
--- /dev/null
+++ b/include/linux/fsevent.h
@@ -0,0 +1,33 @@
+/* SPDX-License-Identifier: GPL-2.0-or-later */
+/*
+ * Copyright (c) 2025 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#ifndef _LINUX_FSEVENT_H__
+#define _LINUX_FSEVENT_H__
+
+void fsevent_send_mount(struct super_block *sb, struct kobject *kobject,
+ struct fs_context *fc);
+
+void fsevent_send(struct super_block *sb, struct kobject *kobject,
+ enum kobject_action kaction);
+
+static inline void fsevent_send_unmount(struct super_block *sb,
+ struct kobject *kobject)
+{
+ fsevent_send(sb, kobject, KOBJ_REMOVE);
+}
+
+static inline void fsevent_send_remount(struct super_block *sb,
+ struct kobject *kobject)
+{
+ fsevent_send(sb, kobject, KOBJ_CHANGE);
+}
+
+static inline void fsevent_send_shutdown(struct super_block *sb,
+ struct kobject *kobject)
+{
+ fsevent_send(sb, kobject, KOBJ_OFFLINE);
+}
+
+#endif /* _LINUX_FSEVENT_H__ */
diff --git a/fs/Makefile b/fs/Makefile
index b4d182465405fe..266981bd25ab09 100644
--- a/fs/Makefile
+++ b/fs/Makefile
@@ -158,7 +158,7 @@ obj-y := open.o read_write.o file_table.o super.o \
stack.o fs_struct.o statfs.o fs_pin.o nsfs.o \
fs_dirent.o fs_context.o fs_parser.o fsopen.o init.o \
kernel_read_file.o mnt_idmapping.o remap_range.o pidfs.o \
- file_attr.o fserror.o
+ file_attr.o fserror.o fsevent.o
obj-$(CONFIG_BUFFER_HEAD) += buffer.o mpage.o
obj-$(CONFIG_PROC_FS) += proc_namespace.o
diff --git a/fs/fsevent.c b/fs/fsevent.c
new file mode 100644
index 00000000000000..d37f42f844e057
--- /dev/null
+++ b/fs/fsevent.c
@@ -0,0 +1,126 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2025 Oracle. All Rights Reserved.
+ * Author: Darrick J. Wong <djwong@kernel.org>
+ */
+#include <linux/fs.h>
+#include <linux/kobject.h>
+#include <linux/fs_context.h>
+#include <linux/seq_buf.h>
+#include <linux/fsevent.h>
+
+static inline size_t fs_uevent_bufsize(const struct super_block *sb,
+ const char *source,
+ unsigned int *envlen)
+{
+ size_t ret = sizeof("TYPE=filesystem") +
+ sizeof("SID=") + sizeof_field(struct super_block, s_id);
+ *envlen += 2;
+
+ if (source) {
+ ret += sizeof("SOURCE=") + strlen(source) + 1;
+ (*envlen)++;
+ }
+
+ if (sb->s_uuid_len == sizeof(sb->s_uuid)) {
+ ret += sizeof("UUID=") + UUID_STRING_LEN;
+ (*envlen)++;
+ }
+
+ /* null array element terminator */
+ ret++;
+ (*envlen)++;
+ return ret;
+}
+
+static char **format_uevent_strings(struct super_block *sb, const char *source)
+{
+ struct seq_buf sbuf;
+ unsigned int envlen = 0;
+ size_t buflen = fs_uevent_bufsize(sb, source, &envlen);
+ char *buf;
+ char **env, **envp;
+
+ buf = kzalloc(buflen, GFP_KERNEL);
+ if (!buf)
+ return NULL;
+ env = kcalloc(envlen, sizeof(char *), GFP_KERNEL);
+ if (!env) {
+ kfree(buf);
+ return NULL;
+ }
+
+ seq_buf_init(&sbuf, buf, buflen);
+ envp = env;
+
+ /*
+ * Add a second null terminator on the end so the next printf can start
+ * printing at the second null terminator.
+ */
+ seq_buf_get_buf(&sbuf, envp++);
+ seq_buf_printf(&sbuf, "TYPE=filesystem");
+ seq_buf_putc(&sbuf, 0);
+
+ seq_buf_get_buf(&sbuf, envp++);
+ seq_buf_printf(&sbuf, "SID=%s", sb->s_id);
+ seq_buf_putc(&sbuf, 0);
+
+ if (source) {
+ seq_buf_get_buf(&sbuf, envp++);
+ seq_buf_printf(&sbuf, "SOURCE=%s", source);
+ seq_buf_putc(&sbuf, 0);
+ }
+
+ if (sb->s_uuid_len == sizeof(sb->s_uuid)) {
+ seq_buf_get_buf(&sbuf, envp++);
+ seq_buf_printf(&sbuf, "UUID=%pU", &sb->s_uuid);
+ seq_buf_putc(&sbuf, 0);
+ }
+
+ /* Add null terminator to strings array */
+ *envp = NULL;
+
+ if (seq_buf_has_overflowed(&sbuf)) {
+ WARN_ON(1);
+ kfree(env);
+ kfree(buf);
+ return NULL;
+ }
+
+ return env;
+}
+
+static inline void free_uevent_strings(char **env)
+{
+ kfree(env[0]);
+ kfree(env);
+}
+
+/*
+ * Send a uevent signalling that the mount succeeded so we can use udev rules
+ * to start background services.
+ */
+void fsevent_send_mount(struct super_block *sb, struct kobject *kobject,
+ struct fs_context *fc)
+{
+ char **env = format_uevent_strings(sb, fc->source);
+
+ if (env) {
+ kobject_uevent_env(kobject, KOBJ_ADD, env);
+ free_uevent_strings(env);
+ }
+}
+EXPORT_SYMBOL_GPL(fsevent_send_mount);
+
+/* Send a uevent signalling that something happened to a live mount. */
+void fsevent_send(struct super_block *sb, struct kobject *kobject,
+ enum kobject_action kaction)
+{
+ char **env = format_uevent_strings(sb, NULL);
+
+ if (env) {
+ kobject_uevent_env(kobject, kaction, env);
+ free_uevent_strings(env);
+ }
+}
+EXPORT_SYMBOL_GPL(fsevent_send);
^ permalink raw reply related [flat|nested] 15+ messages in thread* Re: [PATCH 1/4] fs: send uevents for filesystem mount events
2025-12-18 2:04 ` [PATCH 1/4] fs: send uevents for filesystem mount events Darrick J. Wong
2025-12-18 5:26 ` Christoph Hellwig
2025-12-18 23:33 ` [PATCH V4.1 " Darrick J. Wong
@ 2025-12-24 12:47 ` Christian Brauner
2025-12-26 23:58 ` Ian Kent
2026-01-05 17:25 ` Darrick J. Wong
2 siblings, 2 replies; 15+ messages in thread
From: Christian Brauner @ 2025-12-24 12:47 UTC (permalink / raw)
To: Darrick J. Wong; +Cc: linux-ext4, linux-xfs, hch, linux-fsdevel
On Wed, Dec 17, 2025 at 06:04:29PM -0800, Darrick J. Wong wrote:
> From: Darrick J. Wong <djwong@kernel.org>
>
> Add the ability to send uevents whenever a filesystem mounts, unmounts,
> or goes down. This will enable XFS to start daemons whenever a
> filesystem is first mounted.
>
> Regrettably, we can't wire this directly into get_tree_bdev_flags or
> generic_shutdown_super because not all filesystems set up a kobject
> representation in sysfs, and the VFS has no idea if a filesystem
> actually does that.
>
> Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
> ---
I have issues with uevents as a mechanism for this. Uevents are tied to
network namespaces and they are not really namespaced appropriately. Any
filesystem that hooks into this mechanism will spew uevents into the
initial network namespace unconditionally. Any container mountable
filesystem that wants to use this interface will spam the host with
this event though the even is completely useless without appropriate
meta information about the relevant mount namespaces and further
parameters. This is a design dead end going forward imho. So please
let's not do this.
Instead ties this to fanotify which is the right interface for this.
My suggestion would be to tie this to mount namespaces as that's the
appropriate object. Fanotify already supports listening for general
mount/umount events on mount namespaces. So extend it to send filesystem
creation/destruction events so that a caller may listen on the initial
mount namespace - where xfs fses can be mounted - you could even make it
filterable per filesystem type right away.
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 1/4] fs: send uevents for filesystem mount events
2025-12-24 12:47 ` [PATCH " Christian Brauner
@ 2025-12-26 23:58 ` Ian Kent
2026-01-05 17:26 ` Darrick J. Wong
2026-01-05 17:25 ` Darrick J. Wong
1 sibling, 1 reply; 15+ messages in thread
From: Ian Kent @ 2025-12-26 23:58 UTC (permalink / raw)
To: Christian Brauner, Darrick J. Wong
Cc: linux-ext4, linux-xfs, hch, linux-fsdevel
On 24/12/25 20:47, Christian Brauner wrote:
> On Wed, Dec 17, 2025 at 06:04:29PM -0800, Darrick J. Wong wrote:
>> From: Darrick J. Wong <djwong@kernel.org>
>>
>> Add the ability to send uevents whenever a filesystem mounts, unmounts,
>> or goes down. This will enable XFS to start daemons whenever a
>> filesystem is first mounted.
>>
>> Regrettably, we can't wire this directly into get_tree_bdev_flags or
>> generic_shutdown_super because not all filesystems set up a kobject
>> representation in sysfs, and the VFS has no idea if a filesystem
>> actually does that.
>>
>> Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
>> ---
> I have issues with uevents as a mechanism for this. Uevents are tied to
> network namespaces and they are not really namespaced appropriately. Any
> filesystem that hooks into this mechanism will spew uevents into the
> initial network namespace unconditionally. Any container mountable
> filesystem that wants to use this interface will spam the host with
> this event though the even is completely useless without appropriate
> meta information about the relevant mount namespaces and further
> parameters. This is a design dead end going forward imho. So please
> let's not do this.
>
> Instead ties this to fanotify which is the right interface for this.
> My suggestion would be to tie this to mount namespaces as that's the
> appropriate object. Fanotify already supports listening for general
> mount/umount events on mount namespaces. So extend it to send filesystem
> creation/destruction events so that a caller may listen on the initial
> mount namespace - where xfs fses can be mounted - you could even make it
> filterable per filesystem type right away.
Seconded, there are way too many sources of mount events for them to not
be specific and targeted.
Just my opinion, ;),
Ian
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 1/4] fs: send uevents for filesystem mount events
2025-12-26 23:58 ` Ian Kent
@ 2026-01-05 17:26 ` Darrick J. Wong
0 siblings, 0 replies; 15+ messages in thread
From: Darrick J. Wong @ 2026-01-05 17:26 UTC (permalink / raw)
To: Ian Kent; +Cc: Christian Brauner, linux-ext4, linux-xfs, hch, linux-fsdevel
On Sat, Dec 27, 2025 at 07:58:10AM +0800, Ian Kent wrote:
>
> On 24/12/25 20:47, Christian Brauner wrote:
> > On Wed, Dec 17, 2025 at 06:04:29PM -0800, Darrick J. Wong wrote:
> > > From: Darrick J. Wong <djwong@kernel.org>
> > >
> > > Add the ability to send uevents whenever a filesystem mounts, unmounts,
> > > or goes down. This will enable XFS to start daemons whenever a
> > > filesystem is first mounted.
> > >
> > > Regrettably, we can't wire this directly into get_tree_bdev_flags or
> > > generic_shutdown_super because not all filesystems set up a kobject
> > > representation in sysfs, and the VFS has no idea if a filesystem
> > > actually does that.
> > >
> > > Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
> > > ---
> > I have issues with uevents as a mechanism for this. Uevents are tied to
> > network namespaces and they are not really namespaced appropriately. Any
> > filesystem that hooks into this mechanism will spew uevents into the
> > initial network namespace unconditionally. Any container mountable
> > filesystem that wants to use this interface will spam the host with
> > this event though the even is completely useless without appropriate
> > meta information about the relevant mount namespaces and further
> > parameters. This is a design dead end going forward imho. So please
> > let's not do this.
> >
> > Instead ties this to fanotify which is the right interface for this.
> > My suggestion would be to tie this to mount namespaces as that's the
> > appropriate object. Fanotify already supports listening for general
> > mount/umount events on mount namespaces. So extend it to send filesystem
> > creation/destruction events so that a caller may listen on the initial
> > mount namespace - where xfs fses can be mounted - you could even make it
> > filterable per filesystem type right away.
>
> Seconded, there are way too many sources of mount events for them to not
>
> be specific and targeted.
NP, thanks to you both for the quick feedback. :)
--D
>
> Just my opinion, ;),
>
> Ian
>
>
^ permalink raw reply [flat|nested] 15+ messages in thread
* Re: [PATCH 1/4] fs: send uevents for filesystem mount events
2025-12-24 12:47 ` [PATCH " Christian Brauner
2025-12-26 23:58 ` Ian Kent
@ 2026-01-05 17:25 ` Darrick J. Wong
1 sibling, 0 replies; 15+ messages in thread
From: Darrick J. Wong @ 2026-01-05 17:25 UTC (permalink / raw)
To: Christian Brauner; +Cc: linux-ext4, linux-xfs, hch, linux-fsdevel
On Wed, Dec 24, 2025 at 01:47:25PM +0100, Christian Brauner wrote:
> On Wed, Dec 17, 2025 at 06:04:29PM -0800, Darrick J. Wong wrote:
> > From: Darrick J. Wong <djwong@kernel.org>
> >
> > Add the ability to send uevents whenever a filesystem mounts, unmounts,
> > or goes down. This will enable XFS to start daemons whenever a
> > filesystem is first mounted.
> >
> > Regrettably, we can't wire this directly into get_tree_bdev_flags or
> > generic_shutdown_super because not all filesystems set up a kobject
> > representation in sysfs, and the VFS has no idea if a filesystem
> > actually does that.
> >
> > Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
> > ---
>
> I have issues with uevents as a mechanism for this. Uevents are tied to
> network namespaces and they are not really namespaced appropriately. Any
> filesystem that hooks into this mechanism will spew uevents into the
> initial network namespace unconditionally. Any container mountable
> filesystem that wants to use this interface will spam the host with
> this event though the even is completely useless without appropriate
> meta information about the relevant mount namespaces and further
> parameters. This is a design dead end going forward imho. So please
> let's not do this.
Ok. Initially I'd assumed that any xfs mounts would have to be made
initially by whatever's managing the containers and then bindmounted
into an actual container, but fanotify in the associated mountns means
that containers could decide to have their own healer instances with
their own policies.
It had also occurred to me that wouldn't work so well for a
PrivateMounts=yes systemd service that also gets to mount its own xfs
filesystems. Granted fanotify might not either, but at least this way
we don't have to wind through udev.
> Instead ties this to fanotify which is the right interface for this.
> My suggestion would be to tie this to mount namespaces as that's the
> appropriate object. Fanotify already supports listening for general
> mount/umount events on mount namespaces. So extend it to send filesystem
> creation/destruction events so that a caller may listen on the initial
> mount namespace - where xfs fses can be mounted - you could even make it
> filterable per filesystem type right away.
Hrmm, would that program look something like this? Please ignore the
weird weakhandle struct, I hastily stapled this together from various
programs.
I'm not that familiar with fanotify, so I'm curious what the rest of you
think of handle_mount_event and main. In my trivial workstation test it
worked as a POC, but I've not even thrown fstests at it.
--D
#include <errno.h>
#include <err.h>
#include <stdlib.h>
#include <stdio.h>
#include <fcntl.h>
#include <sys/fanotify.h>
#include <sys/types.h>
#include <unistd.h>
#include <linux/mount.h>
#include <sys/syscall.h>
#include <string.h>
#include <sys/wait.h>
#include <limits.h>
struct weakhandle {
const char *mntpoint;
};
/* Compute the systemd instance unit name for this mountpoint. */
int
weakhandle_instance_unit_name(
struct weakhandle *wh,
const char *template,
char *unitname,
size_t unitnamelen)
{
FILE *fp;
char *s;
ssize_t bytes;
pid_t child_pid;
int pipe_fds[2];
int ret;
ret = pipe(pipe_fds);
if (ret)
return -1;
child_pid = fork();
if (child_pid < 0)
return -1;
if (!child_pid) {
/* child process */
char *argv[] = {
"systemd-escape",
"--template",
(char *)template,
"--path",
(char *)wh->mntpoint,
NULL,
};
ret = dup2(pipe_fds[1], STDOUT_FILENO);
if (ret < 0) {
perror(wh->mntpoint);
goto fail;
}
ret = execvp("systemd-escape", argv);
if (ret)
perror(wh->mntpoint);
fail:
exit(EXIT_FAILURE);
}
/* parent scrapes the output */
fp = fdopen(pipe_fds[0], "r");
s = fgets(unitname, unitnamelen, fp);
fclose(fp);
close(pipe_fds[1]);
waitpid(child_pid, NULL, 0);
if (!s) {
errno = ENOENT;
return -1;
}
/* trim off trailing newline */
bytes = strlen(s);
if (s[bytes - 1] == '\n')
s[bytes - 1] = 0;
return 0;
}
static void start_healer(const char *mntpoint)
{
struct weakhandle wh = {
.mntpoint = mntpoint,
};
char svcname[PATH_MAX];
pid_t child_pid;
int child_status;
int ret;
ret = weakhandle_instance_unit_name(&wh, "xfs_healer@.service",
svcname, PATH_MAX);
if (ret) {
perror("whiun!");
return;
}
printf("systemctl start xfs_healer@%s\n", svcname);
child_pid = fork();
if (child_pid < 0) {
perror(mntpoint);
return;
}
if (!child_pid) {
/* child starts the process */
char *argv[] = {
"systemctl",
"start",
"--no-block",
svcname,
NULL,
};
ret = execvp("systemctl", argv);
if (ret)
perror("systemctl");
exit(EXIT_FAILURE);
}
/* parent waits for process */
waitpid(child_pid, &child_status, 0);
if (WIFEXITED(child_status) && WEXITSTATUS(child_status) == 0) {
printf("%s: healer started\n", mntpoint);
fflush(stdout);
return;
}
fprintf(stderr, "%s: could not start healer\n", mntpoint);
}
static void find_mount(const struct fanotify_event_info_mnt *mnt,
int mnt_ns_fd)
{
struct mnt_id_req req = {
.size = sizeof(req),
.mnt_id = mnt->mnt_id,
.mnt_ns_fd = mnt_ns_fd,
.param = STATMOUNT_FS_TYPE | STATMOUNT_MNT_POINT,
};
size_t smbuf_size = sizeof(struct statmount) + 4096;
struct statmount *smbuf = alloca(smbuf_size);
int ret;
ret = syscall(SYS_statmount, &req, smbuf, smbuf_size, 0);
if (ret) {
perror("statmount");
return;
}
printf("mount: id 0x%llx fstype %s mountpoint %s\n", mnt->mnt_id,
smbuf->str + smbuf->fs_type,
smbuf->str + smbuf->mnt_point);
if (!strcmp(smbuf->str + smbuf->fs_type, "xfs"))
start_healer(smbuf->str + smbuf->mnt_point);
}
static void handle_mount_event(const struct fanotify_event_metadata *event,
int mnt_ns_fd)
{
const struct fanotify_event_info_header *info;
const struct fanotify_event_info_mnt *mnt;
int off;
if (event->fd != FAN_NOFD) {
printf("Unexpected fd (!= FAN_NOFD)\n");
return;
}
switch (event->mask) {
case FAN_MNT_ATTACH:
printf("FAN_MNT_ATTACH (len=%d)\n", event->event_len);
break;
case FAN_MNT_DETACH:
printf("FAN_MNT_DETACH (len=%d)\n", event->event_len);
break;
}
for (off = sizeof(*event) ; off < event->event_len;
off += info->len) {
info = (struct fanotify_event_info_header *)
((char *) event + off);
switch (info->info_type) {
case FAN_EVENT_INFO_TYPE_MNT:
mnt = (struct fanotify_event_info_mnt *) info;
printf("\tGeneric Mount Record: len=%d\n",
mnt->hdr.len);
printf("\tmnt_id: %llx\n", mnt->mnt_id);
find_mount(mnt, mnt_ns_fd);
break;
default:
printf("\tUnknown info type=%d len=%d:\n",
info->info_type, info->len);
}
}
}
static void handle_notifications(char *buffer, int len, int mnt_ns_fd)
{
struct fanotify_event_metadata *event =
(struct fanotify_event_metadata *) buffer;
for (; FAN_EVENT_OK(event, len); event = FAN_EVENT_NEXT(event, len)) {
switch (event->mask) {
case FAN_MNT_ATTACH:
case FAN_MNT_DETACH:
handle_mount_event(event, mnt_ns_fd);
break;
default:
printf("unexpected FAN MARK: %llx\n",
(unsigned long long)event->mask);
break;
}
printf("---\n\n");
fflush(stdout);
}
}
int main(int argc, char *argv[])
{
char buffer[BUFSIZ];
int mnt_ns_fd;
int fan_fd;
int ret;
mnt_ns_fd = open("/proc/self/ns/mnt", O_RDONLY);
if (mnt_ns_fd < 0) {
perror("/proc/self/ns/mnt");
return -1;
}
fan_fd = fanotify_init(FAN_REPORT_MNT, O_RDONLY);
if (fan_fd < 0) {
perror("fanotify_init");
return -1;
}
ret = fanotify_mark(fan_fd, FAN_MARK_ADD | FAN_MARK_MNTNS,
FAN_MNT_ATTACH | FAN_MNT_DETACH, mnt_ns_fd, NULL);
if (ret) {
perror("fanotify_mark");
return -1;
}
printf("fanotify active\n");
fflush(stdout);
while (1) {
int n = read(fan_fd, buffer, BUFSIZ);
if (n < 0)
errx(1, "read");
handle_notifications(buffer, n, mnt_ns_fd);
}
return 0;
}
^ permalink raw reply [flat|nested] 15+ messages in thread
* [PATCH 2/4] xfs: send uevents when major filesystem events happen
2025-12-18 2:02 [PATCHSET V4 2/2] fs: send uevents on mount and unmount Darrick J. Wong
2025-12-18 2:04 ` [PATCH 1/4] fs: send uevents for filesystem mount events Darrick J. Wong
@ 2025-12-18 2:04 ` Darrick J. Wong
2025-12-18 5:27 ` Christoph Hellwig
2025-12-18 2:05 ` [PATCH 3/4] ext4: convert ext4_root to a kset Darrick J. Wong
2025-12-18 2:05 ` [PATCH 4/4] ext4: send uevents when major filesystem events happen Darrick J. Wong
3 siblings, 1 reply; 15+ messages in thread
From: Darrick J. Wong @ 2025-12-18 2:04 UTC (permalink / raw)
To: brauner, djwong; +Cc: linux-ext4, linux-xfs, hch, linux-fsdevel
From: Darrick J. Wong <djwong@kernel.org>
Send uevents when we mount, unmount, and shut down the filesystem, so
that we can trigger systemd services when major events happen. This
enables us to create a udev rule that will start up xfs_healer whenever
a filesystem is mounted.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
fs/xfs/xfs_fsops.c | 2 ++
fs/xfs/xfs_super.c | 10 ++++++++++
2 files changed, 12 insertions(+)
diff --git a/fs/xfs/xfs_fsops.c b/fs/xfs/xfs_fsops.c
index b7c21f68edc78d..bd1022ecd2eb4c 100644
--- a/fs/xfs/xfs_fsops.c
+++ b/fs/xfs/xfs_fsops.c
@@ -27,6 +27,7 @@
#include "xfs_metafile.h"
#include <linux/fserror.h>
+#include <linux/fsevent.h>
/*
* Write new AG headers to disk. Non-transactional, but need to be
@@ -544,6 +545,7 @@ xfs_do_force_shutdown(
xfs_stack_trace();
fserror_report_shutdown(mp->m_super, GFP_KERNEL);
+ fsevent_send_shutdown(mp->m_super, &mp->m_kobj.kobject);
}
/*
diff --git a/fs/xfs/xfs_super.c b/fs/xfs/xfs_super.c
index bc71aa9dcee8d6..bd078c99389bc1 100644
--- a/fs/xfs/xfs_super.c
+++ b/fs/xfs/xfs_super.c
@@ -53,6 +53,8 @@
#include <linux/magic.h>
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
+#include <linux/uuid.h>
+#include <linux/fsevent.h>
static const struct super_operations xfs_super_operations;
@@ -1249,6 +1251,8 @@ xfs_fs_put_super(
{
struct xfs_mount *mp = XFS_M(sb);
+ fsevent_send_unmount(mp->m_super, &mp->m_kobj.kobject);
+
xfs_notice(mp, "Unmounting Filesystem %pU", &mp->m_sb.sb_uuid);
xfs_filestream_unmount(mp);
xfs_unmountfs(mp);
@@ -1972,6 +1976,11 @@ xfs_fs_fill_super(
goto out_unmount;
}
+ /*
+ * Send a uevent signalling that the mount succeeded so we can use udev
+ * rules to start background services.
+ */
+ fsevent_send_mount(mp->m_super, &mp->m_kobj.kobject, fc);
return 0;
out_filestream_unmount:
@@ -2217,6 +2226,7 @@ xfs_fs_reconfigure(
return error;
}
+ fsevent_send_remount(mp->m_super, &mp->m_kobj.kobject);
return 0;
}
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH 3/4] ext4: convert ext4_root to a kset
2025-12-18 2:02 [PATCHSET V4 2/2] fs: send uevents on mount and unmount Darrick J. Wong
2025-12-18 2:04 ` [PATCH 1/4] fs: send uevents for filesystem mount events Darrick J. Wong
2025-12-18 2:04 ` [PATCH 2/4] xfs: send uevents when major filesystem events happen Darrick J. Wong
@ 2025-12-18 2:05 ` Darrick J. Wong
2025-12-18 5:27 ` Christoph Hellwig
2025-12-18 2:05 ` [PATCH 4/4] ext4: send uevents when major filesystem events happen Darrick J. Wong
3 siblings, 1 reply; 15+ messages in thread
From: Darrick J. Wong @ 2025-12-18 2:05 UTC (permalink / raw)
To: brauner, djwong; +Cc: linux-ext4, linux-xfs, hch, linux-fsdevel
From: Darrick J. Wong <djwong@kernel.org>
In the next patch, we'll enhance ext4 to send uevents when significant
filesystem mount events occur (remounting, shutdown, unmount, etc.)
However, kobject_uevent_env requires that the filesystem's kobject be a
member of a kset. Therefore, change ext4_root to ext4_kset and migrate
the two users of it.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
fs/ext4/sysfs.c | 20 +++++++++++---------
1 file changed, 11 insertions(+), 9 deletions(-)
diff --git a/fs/ext4/sysfs.c b/fs/ext4/sysfs.c
index 0018e09b867ec3..e6a14585244308 100644
--- a/fs/ext4/sysfs.c
+++ b/fs/ext4/sysfs.c
@@ -564,7 +564,7 @@ void ext4_notify_error_sysfs(struct ext4_sb_info *sbi)
sysfs_notify(&sbi->s_kobj, NULL, "errors_count");
}
-static struct kobject *ext4_root;
+static struct kset *ext4_kset;
static struct kobject *ext4_feat;
@@ -574,7 +574,8 @@ int ext4_register_sysfs(struct super_block *sb)
int err;
init_completion(&sbi->s_kobj_unregister);
- err = kobject_init_and_add(&sbi->s_kobj, &ext4_sb_ktype, ext4_root,
+ sbi->s_kobj.kset = ext4_kset;
+ err = kobject_init_and_add(&sbi->s_kobj, &ext4_sb_ktype, NULL,
"%s", sb->s_id);
if (err) {
kobject_put(&sbi->s_kobj);
@@ -615,8 +616,8 @@ int __init ext4_init_sysfs(void)
{
int ret;
- ext4_root = kobject_create_and_add("ext4", fs_kobj);
- if (!ext4_root)
+ ext4_kset = kset_create_and_add("ext4", NULL, fs_kobj);
+ if (!ext4_kset)
return -ENOMEM;
ext4_feat = kzalloc(sizeof(*ext4_feat), GFP_KERNEL);
@@ -625,8 +626,9 @@ int __init ext4_init_sysfs(void)
goto root_err;
}
+ ext4_feat->kset = ext4_kset;
ret = kobject_init_and_add(ext4_feat, &ext4_feat_ktype,
- ext4_root, "features");
+ NULL, "features");
if (ret)
goto feat_err;
@@ -637,8 +639,8 @@ int __init ext4_init_sysfs(void)
kobject_put(ext4_feat);
ext4_feat = NULL;
root_err:
- kobject_put(ext4_root);
- ext4_root = NULL;
+ kset_unregister(ext4_kset);
+ ext4_kset = NULL;
return ret;
}
@@ -646,8 +648,8 @@ void ext4_exit_sysfs(void)
{
kobject_put(ext4_feat);
ext4_feat = NULL;
- kobject_put(ext4_root);
- ext4_root = NULL;
+ kset_unregister(ext4_kset);
+ ext4_kset = NULL;
remove_proc_entry(proc_dirname, NULL);
ext4_proc_root = NULL;
}
^ permalink raw reply related [flat|nested] 15+ messages in thread* [PATCH 4/4] ext4: send uevents when major filesystem events happen
2025-12-18 2:02 [PATCHSET V4 2/2] fs: send uevents on mount and unmount Darrick J. Wong
` (2 preceding siblings ...)
2025-12-18 2:05 ` [PATCH 3/4] ext4: convert ext4_root to a kset Darrick J. Wong
@ 2025-12-18 2:05 ` Darrick J. Wong
2025-12-18 5:27 ` Christoph Hellwig
3 siblings, 1 reply; 15+ messages in thread
From: Darrick J. Wong @ 2025-12-18 2:05 UTC (permalink / raw)
To: brauner, djwong; +Cc: linux-ext4, linux-xfs, hch, linux-fsdevel
From: Darrick J. Wong <djwong@kernel.org>
Send uevents when we mount, unmount, and shut down the filesystem, so
that people can trigger systemd services when major events happen.
Signed-off-by: "Darrick J. Wong" <djwong@kernel.org>
---
fs/ext4/ioctl.c | 2 ++
fs/ext4/super.c | 6 ++++++
fs/ext4/sysfs.c | 2 ++
3 files changed, 10 insertions(+)
diff --git a/fs/ext4/ioctl.c b/fs/ext4/ioctl.c
index ea26cd03d3ce28..48ea33cdebbf0c 100644
--- a/fs/ext4/ioctl.c
+++ b/fs/ext4/ioctl.c
@@ -27,6 +27,7 @@
#include "fsmap.h"
#include <trace/events/ext4.h>
#include <linux/fserror.h>
+#include <linux/fsevent.h>
typedef void ext4_update_sb_callback(struct ext4_sb_info *sbi,
struct ext4_super_block *es,
@@ -845,6 +846,7 @@ int ext4_force_shutdown(struct super_block *sb, u32 flags)
return -EINVAL;
}
clear_opt(sb, DISCARD);
+ fsevent_send_shutdown(sb, &sbi->s_kobj);
fserror_report_shutdown(sb, GFP_KERNEL);
return 0;
}
diff --git a/fs/ext4/super.c b/fs/ext4/super.c
index a6241ffb8639c3..649eed02d45dbb 100644
--- a/fs/ext4/super.c
+++ b/fs/ext4/super.c
@@ -49,6 +49,7 @@
#include <linux/fs_context.h>
#include <linux/fs_parser.h>
#include <linux/fserror.h>
+#include <linux/fsevent.h>
#include "ext4.h"
#include "ext4_extents.h" /* Needed for trace points definition */
@@ -1284,6 +1285,8 @@ static void ext4_put_super(struct super_block *sb)
int aborted = 0;
int err;
+ fsevent_send_unmount(sb, &sbi->s_kobj);
+
/*
* Unregister sysfs before destroying jbd2 journal.
* Since we could still access attr_journal_task attribute via sysfs
@@ -5698,6 +5701,7 @@ static int __ext4_fill_super(struct fs_context *fc, struct super_block *sb)
if (err)
goto failed_mount9;
+ fsevent_send_mount(sb, &sbi->s_kobj, fc);
return 0;
failed_mount9:
@@ -6835,6 +6839,7 @@ static int __ext4_remount(struct fs_context *fc, struct super_block *sb)
static int ext4_reconfigure(struct fs_context *fc)
{
struct super_block *sb = fc->root->d_sb;
+ struct ext4_sb_info *sbi = EXT4_SB(sb);
int ret;
bool old_ro = sb_rdonly(sb);
@@ -6852,6 +6857,7 @@ static int ext4_reconfigure(struct fs_context *fc)
&sb->s_uuid,
(old_ro != sb_rdonly(sb)) ? (sb_rdonly(sb) ? " ro" : " r/w") : "");
+ fsevent_send_remount(sb, &sbi->s_kobj);
return 0;
}
diff --git a/fs/ext4/sysfs.c b/fs/ext4/sysfs.c
index e6a14585244308..9d9812a5b265ba 100644
--- a/fs/ext4/sysfs.c
+++ b/fs/ext4/sysfs.c
@@ -335,6 +335,7 @@ EXT4_ATTR_FEATURE(encrypted_casefold);
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
EXT4_ATTR_FEATURE(blocksize_gt_pagesize);
#endif
+EXT4_ATTR_FEATURE(uevents);
static struct attribute *ext4_feat_attrs[] = {
ATTR_LIST(lazy_itable_init),
@@ -358,6 +359,7 @@ static struct attribute *ext4_feat_attrs[] = {
#ifdef CONFIG_TRANSPARENT_HUGEPAGE
ATTR_LIST(blocksize_gt_pagesize),
#endif
+ ATTR_LIST(uevents),
NULL,
};
ATTRIBUTE_GROUPS(ext4_feat);
^ permalink raw reply related [flat|nested] 15+ messages in thread