diff --git a/fs/mount_notify.c b/fs/mount_notify.c
index 365aac5fa746..8fcd8b5b8b56 100644
--- a/fs/mount_notify.c
+++ b/fs/mount_notify.c
@@ -93,6 +93,8 @@ void notify_mount(struct mount *trigger,
n.watch.info = info_flags | watch_sizeof(n);
n.triggered_on = trigger->mnt_id;
+ smp_wmb(); /* See fsinfo_generic_mount_info(). */
+
switch (subtype) {
case NOTIFY_MOUNT_EXPIRY:
case NOTIFY_MOUNT_READONLY:diff --git a/fs/namespace.c b/fs/namespace.c
index b5c2a3b4f96d..2205b1e52a41 100644
--- a/fs/namespace.c
+++ b/fs/namespace.c
@@ -4282,6 +4282,17 @@ int fsinfo_generic_mount_info(struct path *path, struct fsinfo_context *ctx)
p->mnt_unique_id = m->mnt_unique_id;
p->mnt_id = m->mnt_id;
+#ifdef CONFIG_MOUNT_NOTIFICATIONS
+ p->mnt_subtree_notifications = atomic_read(&m->mnt_subtree_notifications);
+ p->mnt_topology_changes = atomic_read(&m->mnt_topology_changes);
+ p->mnt_attr_changes = atomic_read(&m->mnt_attr_changes);
+#endif
+
+ /* Record the counters before reading the attributes as we're not
+ * holding a lock. Paired with a write barrier in notify_mount().
+ */
+ smp_rmb();
+
flags = READ_ONCE(m->mnt.mnt_flags);
if (flags & MNT_READONLY)
p->attr |= MOUNT_ATTR_RDONLY;
@@ -4319,6 +4330,9 @@ int fsinfo_generic_mount_topology(struct path *path, struct fsinfo_context *ctx)
m = real_mount(path->mnt);
+#ifdef CONFIG_MOUNT_NOTIFICATIONS
+ p->mnt_topology_changes = atomic_read(&m->mnt_topology_changes);
+#endif
p->parent_id = m->mnt_parent->mnt_id;
if (path->mnt == root.mnt) {@@ -4445,6 +4459,13 @@ static void fsinfo_store_mount(struct fsinfo_context *ctx, const struct mount *p
record.mnt_unique_id = p->mnt_unique_id;
record.mnt_id = p->mnt_id;
record.parent_id = is_root ? p->mnt_id : p->mnt_parent->mnt_id;
+
+#ifdef CONFIG_MOUNT_NOTIFICATIONS
+ record.mnt_notify_sum = (atomic_read(&p->mnt_attr_changes) +
+ atomic_read(&p->mnt_topology_changes) +
+ atomic_read(&p->mnt_subtree_notifications));
+#endif
+
memcpy(ctx->buffer + usage, &record, sizeof(record));
}
diff --git a/include/uapi/linux/fsinfo.h b/include/uapi/linux/fsinfo.h
index f0a352b7028e..5cf1dad3c6c9 100644
--- a/include/uapi/linux/fsinfo.h
+++ b/include/uapi/linux/fsinfo.h
@@ -100,6 +100,10 @@ struct fsinfo_mount_info {
__u64 mnt_unique_id; /* Kernel-lifetime unique mount ID */
__u32 mnt_id; /* Mount identifier (use with AT_FSINFO_MOUNTID_PATH) */
__u32 attr; /* MOUNT_ATTR_* flags */
+ __u32 mnt_attr_changes; /* Number of attribute changes to this mount. */
+ __u32 mnt_topology_changes; /* Number of topology changes to this mount. */
+ __u32 mnt_subtree_notifications; /* Number of notifications in mount subtree */
+ __u32 padding[1];
};
#define FSINFO_ATTR_MOUNT_INFO__STRUCT struct fsinfo_mount_info@@ -113,6 +117,7 @@ struct fsinfo_mount_topology {
__u32 dependent_source_id; /* Dependent: source mount group ID */
__u32 dependent_clone_of_id; /* Dependent: ID of mount this was cloned from */
__u32 propagation_type; /* MOUNT_PROPAGATION_* type */
+ __u32 mnt_topology_changes; /* Number of topology changes to this mount. */
};
#define FSINFO_ATTR_MOUNT_TOPOLOGY__STRUCT struct fsinfo_mount_topology@@ -125,6 +130,9 @@ struct fsinfo_mount_child {
__u64 mnt_unique_id; /* Kernel-lifetime unique mount ID */
__u32 mnt_id; /* Mount identifier (use with AT_FSINFO_MOUNTID_PATH) */
__u32 parent_id; /* Parent mount identifier */
+ __u32 mnt_notify_sum; /* Sum of mnt_attr_changes, mnt_topology_changes and
+ * mnt_subtree_notifications.
+ */
};
#define FSINFO_ATTR_MOUNT_CHILDREN__STRUCT struct fsinfo_mount_childdiff --git a/samples/vfs/test-fsinfo.c b/samples/vfs/test-fsinfo.c
index b7290ea8eb55..667a99b82486 100644
--- a/samples/vfs/test-fsinfo.c
+++ b/samples/vfs/test-fsinfo.c
@@ -304,6 +304,8 @@ static void dump_fsinfo_generic_mount_info(void *reply, unsigned int size)
printf("\tmnt_uniq: %llx\n", (unsigned long long)r->mnt_unique_id);
printf("\tmnt_id : %x\n", r->mnt_id);
printf("\tattr : %x\n", r->attr);
+ printf("\tmnt_nfy : attr=%u topology=%u subtree=%u\n",
+ r->mnt_attr_changes, r->mnt_topology_changes, r->mnt_subtree_notifications);
}
static void dump_fsinfo_generic_mount_topology(void *reply, unsigned int size)@@ -332,6 +334,7 @@ static void dump_fsinfo_generic_mount_topology(void *reply, unsigned int size)
break;
}
+ printf("\tmnt_nfy : topology=%u\n", r->mnt_topology_changes);
}
static void dump_fsinfo_generic_mount_children(void *reply, unsigned int size)@@ -354,8 +357,9 @@ static void dump_fsinfo_generic_mount_children(void *reply, unsigned int size)
mp = "<this>";
}
- printf("%8x %16llx %s\n",
- r->mnt_id, (unsigned long long)r->mnt_unique_id, mp);
+ printf("%8x %16llx %10u %s\n",
+ r->mnt_id, (unsigned long long)r->mnt_unique_id,
+ r->mnt_notify_sum, mp);
}
static void dump_string(void *reply, unsigned int size)