[PATCH 16/19] fsinfo: Provide superblock notification counter [ver #16]

From: David Howells
Date: Tue Feb 18 2020 - 12:08:33 EST


Provide an fsinfo attribute to export the superblock notification counter
so that it can be polled in the case of a notification buffer overrun.
This is accessed with:

struct fsinfo_params params = {
.request = FSINFO_ATTR_SB_NOTIFICATIONS,
};

and returns a structure that looks like:

struct fsinfo_sb_notifications {
__u64 watch_id;
__u32 notify_counter;
__u32 __reserved[1];
};

Where watch_id is a number uniquely identifying the superblock in
notification records and notify_counter is incremented for each
superblock notification posted.

Signed-off-by: David Howells <dhowells@xxxxxxxxxx>
---

fs/fsinfo.c | 11 +++++++++++
include/linux/fs.h | 4 ++++
include/uapi/linux/fsinfo.h | 12 ++++++++++++
include/uapi/linux/watch_queue.h | 2 +-
samples/vfs/test-fsinfo.c | 12 ++++++++++--
5 files changed, 38 insertions(+), 3 deletions(-)

diff --git a/fs/fsinfo.c b/fs/fsinfo.c
index b57fbcd3a7a5..72fc4f790a5a 100644
--- a/fs/fsinfo.c
+++ b/fs/fsinfo.c
@@ -226,6 +226,16 @@ static int fsinfo_generic_volume_id(struct path *path, struct fsinfo_context *ct
return fsinfo_string(path->dentry->d_sb->s_id, ctx);
}

+static int fsinfo_generic_sb_notifications(struct path *path, struct fsinfo_context *ctx)
+{
+ struct fsinfo_sb_notifications *p = ctx->buffer;
+ struct super_block *sb = path->dentry->d_sb;
+
+ p->watch_id = sb->s_unique_id;
+ p->notify_counter = atomic_read(&sb->s_notify_counter);
+ return sizeof(*p);
+}
+
static int fsinfo_attribute_info(struct path *path, struct fsinfo_context *ctx)
{
const struct fsinfo_attribute *attr;
@@ -292,6 +302,7 @@ static const struct fsinfo_attribute fsinfo_common_attributes[] = {
FSINFO_STRING (FSINFO_ATTR_VOLUME_ID, fsinfo_generic_volume_id),
FSINFO_VSTRUCT (FSINFO_ATTR_VOLUME_UUID, fsinfo_generic_volume_uuid),
FSINFO_VSTRUCT (FSINFO_ATTR_FEATURES, fsinfo_generic_features),
+ FSINFO_VSTRUCT (FSINFO_ATTR_SB_NOTIFICATIONS, fsinfo_generic_sb_notifications),

FSINFO_VSTRUCT (FSINFO_ATTR_FSINFO, fsinfo_generic_fsinfo),
FSINFO_VSTRUCT_N(FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO, fsinfo_attribute_info),
diff --git a/include/linux/fs.h b/include/linux/fs.h
index 423a6f03cdf8..30b910d591db 100644
--- a/include/linux/fs.h
+++ b/include/linux/fs.h
@@ -1558,6 +1558,7 @@ struct super_block {
#ifdef CONFIG_SB_NOTIFICATIONS
struct watch_list *s_watchers;
#endif
+ atomic_t s_notify_counter;
} __randomize_layout;

/* Helper functions so that in most cases filesystems will
@@ -3677,6 +3678,7 @@ static inline void notify_sb(struct super_block *s,
u32 info)
{
#ifdef CONFIG_SB_NOTIFICATIONS
+ atomic_inc(&s->s_notify_counter);
if (unlikely(s->s_watchers)) {
struct superblock_notification n = {
.watch.type = WATCH_TYPE_SB_NOTIFY,
@@ -3699,6 +3701,7 @@ static inline void notify_sb(struct super_block *s,
static inline int notify_sb_error(struct super_block *s, int error)
{
#ifdef CONFIG_SB_NOTIFICATIONS
+ atomic_inc(&s->s_notify_counter);
if (unlikely(s->s_watchers)) {
struct superblock_error_notification n = {
.s.watch.type = WATCH_TYPE_SB_NOTIFY,
@@ -3722,6 +3725,7 @@ static inline int notify_sb_error(struct super_block *s, int error)
static inline int notify_sb_EQDUOT(struct super_block *s)
{
#ifdef CONFIG_SB_NOTIFICATIONS
+ atomic_inc(&s->s_notify_counter);
if (unlikely(s->s_watchers)) {
struct superblock_notification n = {
.watch.type = WATCH_TYPE_SB_NOTIFY,
diff --git a/include/uapi/linux/fsinfo.h b/include/uapi/linux/fsinfo.h
index 5926b16aac4e..5467f88ca9b0 100644
--- a/include/uapi/linux/fsinfo.h
+++ b/include/uapi/linux/fsinfo.h
@@ -23,6 +23,7 @@
#define FSINFO_ATTR_VOLUME_UUID 0x06 /* Volume UUID (LE uuid) */
#define FSINFO_ATTR_VOLUME_NAME 0x07 /* Volume name (string) */
#define FSINFO_ATTR_FEATURES 0x08 /* Filesystem features (bits) */
+#define FSINFO_ATTR_SB_NOTIFICATIONS 0x09 /* sb_notify() information */

#define FSINFO_ATTR_FSINFO_ATTRIBUTE_INFO 0x100 /* Information about attr N (for path) */
#define FSINFO_ATTR_FSINFO_ATTRIBUTES 0x101 /* List of supported attrs (for path) */
@@ -300,6 +301,17 @@ struct fsinfo_volume_uuid {

#define FSINFO_ATTR_VOLUME_UUID__STRUCT struct fsinfo_volume_uuid

+/*
+ * Information struct for fsinfo(FSINFO_ATTR_SB_NOTIFICATIONS).
+ */
+struct fsinfo_sb_notifications {
+ __u64 watch_id; /* Watch ID for superblock. */
+ __u32 notify_counter; /* Number of notifications. */
+ __u32 __reserved[1];
+};
+
+#define FSINFO_ATTR_SB_NOTIFICATIONS__STRUCT struct fsinfo_sb_notifications
+
/*
* Information struct for fsinfo(FSINFO_ATTR_AFS_SERVER_ADDRESSES).
*
diff --git a/include/uapi/linux/watch_queue.h b/include/uapi/linux/watch_queue.h
index 190d27073302..586f4af965ac 100644
--- a/include/uapi/linux/watch_queue.h
+++ b/include/uapi/linux/watch_queue.h
@@ -151,7 +151,7 @@ enum superblock_notification_type {
*/
struct superblock_notification {
struct watch_notification watch; /* WATCH_TYPE_SB_NOTIFY */
- __u64 sb_id; /* 64-bit superblock ID [fsinfo_ids::f_sb_id] */
+ __u64 sb_id; /* 64-bit superblock ID [fsinfo_sb_notifications::watch_id] */
};

struct superblock_error_notification {
diff --git a/samples/vfs/test-fsinfo.c b/samples/vfs/test-fsinfo.c
index 6ad0f84c4327..fd425c08b00b 100644
--- a/samples/vfs/test-fsinfo.c
+++ b/samples/vfs/test-fsinfo.c
@@ -306,6 +306,15 @@ static void dump_fsinfo_generic_mount_child(void *reply, unsigned int size)
printf("%8x %8x\n", f->mnt_id, f->change_counter);
}

+static void dump_fsinfo_generic_sb_notifications(void *reply, unsigned int size)
+{
+ struct fsinfo_sb_notifications *f = reply;
+
+ printf("\n");
+ printf("\twatch_id: %llx\n", (unsigned long long)f->watch_id);
+ printf("\tnotifs : %llx\n", (unsigned long long)f->notify_counter);
+}
+
static void dump_afs_fsinfo_server_address(void *reply, unsigned int size)
{
struct fsinfo_afs_server_address *f = reply;
@@ -416,12 +425,11 @@ static const struct fsinfo_attribute fsinfo_attributes[] = {
FSINFO_STRING (FSINFO_ATTR_VOLUME_ID, fsinfo_generic_volume_id),
FSINFO_VSTRUCT (FSINFO_ATTR_VOLUME_UUID, fsinfo_generic_volume_uuid),
FSINFO_STRING (FSINFO_ATTR_VOLUME_NAME, fsinfo_generic_volume_name),
-
+ FSINFO_VSTRUCT (FSINFO_ATTR_SB_NOTIFICATIONS, fsinfo_generic_sb_notifications),
FSINFO_VSTRUCT (FSINFO_ATTR_MOUNT_INFO, fsinfo_generic_mount_info),
FSINFO_STRING (FSINFO_ATTR_MOUNT_DEVNAME, fsinfo_generic_mount_devname),
FSINFO_LIST (FSINFO_ATTR_MOUNT_CHILDREN, fsinfo_generic_mount_child),
FSINFO_STRING_N (FSINFO_ATTR_MOUNT_POINT, fsinfo_generic_mount_point),
-
FSINFO_STRING (FSINFO_ATTR_AFS_CELL_NAME, afs_cell_name),
FSINFO_STRING (FSINFO_ATTR_AFS_SERVER_NAME, afs_server_name),
FSINFO_LIST_N (FSINFO_ATTR_AFS_SERVER_ADDRESSES, afs_fsinfo_server_address),