@@ -1,4 +1,4 @@
# SPDX-License-Identifier: GPL-2.0-only
-userprogs-always-y += watch_test
+userprogs-always-y += watch_test watch_sb
userccflags += -I usr/include
new file mode 100644
@@ -0,0 +1,114 @@
+// SPDX-License-Identifier: GPL-2.0
+/* Use watch_sb to watch for SB notifications.
+ *
+ * Copyright (C) 2020 Collabora Ltd.
+ * Written by Gabriel Krisman Bertazi <krisman@collabora.com>
+ * Based on watch_test.c by David Howells (dhowells@redhat.com)
+ */
+
+#include <stdio.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <err.h>
+#include <string.h>
+#include<sys/ioctl.h>
+#include <linux/watch_queue.h>
+
+#ifndef __NR_watch_sb
+# define __NR_watch_sb 441
+#endif
+
+static void consumer(int fd)
+{
+ unsigned char buffer[433], *p, *end;
+ union {
+ struct watch_notification n;
+ unsigned char buf1[128];
+ struct superblock_error_notification sen;
+ struct superblock_warning_notification swn;
+ struct superblock_msg_notification smn;
+ } n;
+ ssize_t buf_len;
+
+ for (;;) {
+ buf_len = read(fd, buffer, sizeof(buffer));
+ if (buf_len == -1)
+ err(1, "read");
+
+ if (buf_len == 0) {
+ printf("-- END --\n");
+ return;
+ }
+
+ if (buf_len > sizeof(buffer)) {
+ err(1, "Read buffer overrun: %zd\n", buf_len);
+ return;
+ }
+
+ printf("read() = %zd\n", buf_len);
+
+ p = buffer;
+ end = buffer + buf_len;
+ while (p < end) {
+ size_t largest, len;
+
+ largest = end - p;
+ if (largest > 128)
+ largest = 128;
+ if (largest < sizeof(struct watch_notification))
+ err(1, "Short message header: %zu\n", largest);
+
+ memcpy(&n, p, largest);
+
+ printf("NOTIFY[%03zx]: ty=%06x sy=%02x i=%08x\n",
+ p - buffer, n.n.type, n.n.subtype, n.n.info);
+
+ len = n.n.info & WATCH_INFO_LENGTH;
+ if (len < sizeof(n.n) || len > largest)
+ err(1, "Bad message length: %zu/%zu\n", len, largest);
+
+ switch (n.n.subtype) {
+ case NOTIFY_SUPERBLOCK_ERROR:
+ printf("\t SB AT %s:%d ERROR: %d inode=%llu block=%llu\n",
+ n.sen.function, n.sen.line, n.sen.error_number,
+ n.sen.inode, n.sen.block);
+ if (len > sizeof(n.sen))
+ printf("description: %s\n", n.sen.desc);
+ break;
+ case NOTIFY_SUPERBLOCK_MSG:
+ printf("\t Ext4 MSG: %s\n", n.smn.desc);
+ break;
+ case NOTIFY_SUPERBLOCK_WARNING:
+ printf("\t SB AT %s:%d WARNING inode=%llu block=%llu\n",
+ n.swn.function, n.swn.line, n.swn.inode, n.swn.block);
+ if (len > sizeof(n.sen))
+ printf("description: %s\n", n.swn.desc);
+ break;
+ default:
+ printf("unknown subtype %c\n", n.n.subtype);
+ }
+ p += len;
+ }
+ }
+}
+
+int main (int argc, char **argv)
+{
+ int fd[2];
+
+ if (argc != 2)
+ errx(1, "Missing mount point\n");
+
+ if (syscall(293, fd, O_NOTIFICATION_PIPE) < 0)
+ err(1, "Failed to open pipe\n");
+
+ if (ioctl(fd[0], IOC_WATCH_QUEUE_SET_SIZE, 256) < 0)
+ err(1, "ioctl fail\n");
+
+ if (syscall(__NR_watch_sb, 0, argv[1], NULL, fd[0], 0x3) < 0)
+ err(1, "Failed to watch SB\n");
+
+ consumer(fd[0]);
+
+ return 0;
+}
This sample demonstrates how to use the watch_sb syscall. It exposes notifications like the following: root@host:~# ./watch_sb /mnt read() = 93 NOTIFY[000]: ty=000002 sy=01 i=0300005d SB AT ext4_remount:5636 ERROR: 16 inode=0 block=0 description: Abort forced by user read() = 96 NOTIFY[000]: ty=000002 sy=01 i=03000060 SB AT ext4_lookup:1706 ERROR: 0 inode=13 block=0 description: iget: bogus i_mode (45) Signed-off-by: Gabriel Krisman Bertazi <krisman@collabora.com> --- samples/watch_queue/Makefile | 2 +- samples/watch_queue/watch_sb.c | 114 +++++++++++++++++++++++++++++++++ 2 files changed, 115 insertions(+), 1 deletion(-) create mode 100644 samples/watch_queue/watch_sb.c