All of lore.kernel.org
 help / color / mirror / Atom feed
* Re: [PATCH 02/10] mm: workingset: tell cache transitions from workingset thrashing
From: Johannes Weiner @ 2018-07-23 16:27 UTC (permalink / raw)
  To: Arnd Bergmann
  Cc: Peter Zijlstra, Suren Baghdasaryan, Mike Galbraith, Will Deacon,
	Linux Kernel Mailing List, kernel-team, Linux-MM, Vinayak Menon,
	Ingo Molnar, Shakeel Butt, Catalin Marinas, Tejun Heo, cgroups,
	Andrew Morton, Linus Torvalds, Christopher Lameter, Linux ARM
In-Reply-To: <CAK8P3a15K-TXYuFX-ZsJiroqA1GWX2XS4ioZSjcjJYgh1b_xSA@mail.gmail.com>

On Mon, Jul 23, 2018 at 05:35:35PM +0200, Arnd Bergmann wrote:
> On Mon, Jul 23, 2018 at 5:23 PM, Johannes Weiner <hannes@cmpxchg.org> wrote:
> > From 1d24635a6c7cd395bad5c29a3b9e5d2e98d9ab84 Mon Sep 17 00:00:00 2001
> > From: Johannes Weiner <hannes@cmpxchg.org>
> > Date: Mon, 23 Jul 2018 10:18:23 -0400
> > Subject: [PATCH] arm64: fix vmemmap BUILD_BUG_ON() triggering on !vmemmap
> >  setups
> >
> > Arnd reports the following arm64 randconfig build error with the PSI
> > patches that add another page flag:
> >
> 
> You could add further text here that I had just added to my
> patch description (not sent):
> 
>     Further experiments show that the build error already existed before,
>     but was only triggered with larger values of CONFIG_NR_CPU and/or
>     CONFIG_NODES_SHIFT that might be used in actual configurations but
>     not in randconfig builds.
> 
>     With longer CPU and node masks, I could recreate the problem with
>     kernels as old as linux-4.7 when arm64 NUMA support got added.
> 
>     Cc: stable@vger.kernel.org
>     Fixes: 1a2db300348b ("arm64, numa: Add NUMA support for arm64 platforms.")
>     Fixes: 3e1907d5bf5a ("arm64: mm: move vmemmap region right below
> the linear region")

Sure thing.

> >  arch/arm64/mm/init.c | 4 +++-
> >  1 file changed, 3 insertions(+), 1 deletion(-)
> >
> > diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
> > index 1b18b4722420..72c9b6778b0a 100644
> > --- a/arch/arm64/mm/init.c
> > +++ b/arch/arm64/mm/init.c
> > @@ -611,11 +611,13 @@ void __init mem_init(void)
> >         BUILD_BUG_ON(TASK_SIZE_32                       > TASK_SIZE_64);
> >  #endif
> >
> > +#ifndef CONFIG_SPARSEMEM_VMEMMAP
> >         /*
> 
> I tested it on two broken configurations, and found that you have
> a typo here, it should be 'ifdef', not 'ifndef'. With that change, it
> seems to build fine.
> 
> Tested-by: Arnd Bergmann <arnd@arndb.de>

Thanks for testing it, I don't have a cross-compile toolchain set up.

---

From 34c4c4549f09f971d2d391a8d652d56cb9b05475 Mon Sep 17 00:00:00 2001
From: Johannes Weiner <hannes@cmpxchg.org>
Date: Mon, 23 Jul 2018 10:18:23 -0400
Subject: [PATCH] arm64: fix vmemmap BUILD_BUG_ON() triggering on !vmemmap
 setups

Arnd reports the following arm64 randconfig build error with the PSI
patches that add another page flag:

  /git/arm-soc/arch/arm64/mm/init.c: In function 'mem_init':
  /git/arm-soc/include/linux/compiler.h:357:38: error: call to
  '__compiletime_assert_618' declared with attribute error: BUILD_BUG_ON
  failed: sizeof(struct page) > (1 << STRUCT_PAGE_MAX_SHIFT)

The additional page flag causes other information stored in
page->flags to get bumped into their own struct page member:

  #if SECTIONS_WIDTH+ZONES_WIDTH+NODES_SHIFT+LAST_CPUPID_SHIFT <=
  BITS_PER_LONG - NR_PAGEFLAGS
  #define LAST_CPUPID_WIDTH LAST_CPUPID_SHIFT
  #else
  #define LAST_CPUPID_WIDTH 0
  #endif

  #if defined(CONFIG_NUMA_BALANCING) && LAST_CPUPID_WIDTH == 0
  #define LAST_CPUPID_NOT_IN_PAGE_FLAGS
  #endif

which in turn causes the struct page size to exceed the size set in
STRUCT_PAGE_MAX_SHIFT. This value is an an estimate used to size the
VMEMMAP page array according to address space and struct page size.

However, the check is performed - and triggers here - on a !VMEMMAP
config, which consumes an additional 22 page bits for the sparse
section id. When VMEMMAP is enabled, those bits are returned, cpupid
doesn't need its own member, and the page passes the VMEMMAP check.

Restrict that check to the situation it was meant to check: that we
are sizing the VMEMMAP page array correctly.

Says Arnd:

    Further experiments show that the build error already existed before,
    but was only triggered with larger values of CONFIG_NR_CPU and/or
    CONFIG_NODES_SHIFT that might be used in actual configurations but
    not in randconfig builds.

    With longer CPU and node masks, I could recreate the problem with
    kernels as old as linux-4.7 when arm64 NUMA support got added.

Reported-by: Arnd Bergmann <arnd@arndb.de>
Tested-by: Arnd Bergmann <arnd@arndb.de>
Cc: stable@vger.kernel.org
Fixes: 1a2db300348b ("arm64, numa: Add NUMA support for arm64 platforms.")
Fixes: 3e1907d5bf5a ("arm64: mm: move vmemmap region right below the linear region")
Signed-off-by: Johannes Weiner <hannes@cmpxchg.org>
---
 arch/arm64/mm/init.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
index 1b18b4722420..86d9f9d303b0 100644
--- a/arch/arm64/mm/init.c
+++ b/arch/arm64/mm/init.c
@@ -611,11 +611,13 @@ void __init mem_init(void)
 	BUILD_BUG_ON(TASK_SIZE_32			> TASK_SIZE_64);
 #endif
 
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
 	/*
 	 * Make sure we chose the upper bound of sizeof(struct page)
-	 * correctly.
+	 * correctly when sizing the VMEMMAP array.
 	 */
 	BUILD_BUG_ON(sizeof(struct page) > (1 << STRUCT_PAGE_MAX_SHIFT));
+#endif
 
 	if (PAGE_SIZE >= 16384 && get_num_physpages() <= 128) {
 		extern int sysctl_overcommit_memory;
-- 
2.18.0


^ permalink raw reply related

* [PATCH 02/10] mm: workingset: tell cache transitions from workingset thrashing
From: Johannes Weiner @ 2018-07-23 16:27 UTC (permalink / raw)
  To: linux-arm-kernel
In-Reply-To: <CAK8P3a15K-TXYuFX-ZsJiroqA1GWX2XS4ioZSjcjJYgh1b_xSA@mail.gmail.com>

On Mon, Jul 23, 2018 at 05:35:35PM +0200, Arnd Bergmann wrote:
> On Mon, Jul 23, 2018 at 5:23 PM, Johannes Weiner <hannes@cmpxchg.org> wrote:
> > From 1d24635a6c7cd395bad5c29a3b9e5d2e98d9ab84 Mon Sep 17 00:00:00 2001
> > From: Johannes Weiner <hannes@cmpxchg.org>
> > Date: Mon, 23 Jul 2018 10:18:23 -0400
> > Subject: [PATCH] arm64: fix vmemmap BUILD_BUG_ON() triggering on !vmemmap
> >  setups
> >
> > Arnd reports the following arm64 randconfig build error with the PSI
> > patches that add another page flag:
> >
> 
> You could add further text here that I had just added to my
> patch description (not sent):
> 
>     Further experiments show that the build error already existed before,
>     but was only triggered with larger values of CONFIG_NR_CPU and/or
>     CONFIG_NODES_SHIFT that might be used in actual configurations but
>     not in randconfig builds.
> 
>     With longer CPU and node masks, I could recreate the problem with
>     kernels as old as linux-4.7 when arm64 NUMA support got added.
> 
>     Cc: stable at vger.kernel.org
>     Fixes: 1a2db300348b ("arm64, numa: Add NUMA support for arm64 platforms.")
>     Fixes: 3e1907d5bf5a ("arm64: mm: move vmemmap region right below
> the linear region")

Sure thing.

> >  arch/arm64/mm/init.c | 4 +++-
> >  1 file changed, 3 insertions(+), 1 deletion(-)
> >
> > diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
> > index 1b18b4722420..72c9b6778b0a 100644
> > --- a/arch/arm64/mm/init.c
> > +++ b/arch/arm64/mm/init.c
> > @@ -611,11 +611,13 @@ void __init mem_init(void)
> >         BUILD_BUG_ON(TASK_SIZE_32                       > TASK_SIZE_64);
> >  #endif
> >
> > +#ifndef CONFIG_SPARSEMEM_VMEMMAP
> >         /*
> 
> I tested it on two broken configurations, and found that you have
> a typo here, it should be 'ifdef', not 'ifndef'. With that change, it
> seems to build fine.
> 
> Tested-by: Arnd Bergmann <arnd@arndb.de>

Thanks for testing it, I don't have a cross-compile toolchain set up.

---

>From 34c4c4549f09f971d2d391a8d652d56cb9b05475 Mon Sep 17 00:00:00 2001
From: Johannes Weiner <hannes@cmpxchg.org>
Date: Mon, 23 Jul 2018 10:18:23 -0400
Subject: [PATCH] arm64: fix vmemmap BUILD_BUG_ON() triggering on !vmemmap
 setups

Arnd reports the following arm64 randconfig build error with the PSI
patches that add another page flag:

  /git/arm-soc/arch/arm64/mm/init.c: In function 'mem_init':
  /git/arm-soc/include/linux/compiler.h:357:38: error: call to
  '__compiletime_assert_618' declared with attribute error: BUILD_BUG_ON
  failed: sizeof(struct page) > (1 << STRUCT_PAGE_MAX_SHIFT)

The additional page flag causes other information stored in
page->flags to get bumped into their own struct page member:

  #if SECTIONS_WIDTH+ZONES_WIDTH+NODES_SHIFT+LAST_CPUPID_SHIFT <=
  BITS_PER_LONG - NR_PAGEFLAGS
  #define LAST_CPUPID_WIDTH LAST_CPUPID_SHIFT
  #else
  #define LAST_CPUPID_WIDTH 0
  #endif

  #if defined(CONFIG_NUMA_BALANCING) && LAST_CPUPID_WIDTH == 0
  #define LAST_CPUPID_NOT_IN_PAGE_FLAGS
  #endif

which in turn causes the struct page size to exceed the size set in
STRUCT_PAGE_MAX_SHIFT. This value is an an estimate used to size the
VMEMMAP page array according to address space and struct page size.

However, the check is performed - and triggers here - on a !VMEMMAP
config, which consumes an additional 22 page bits for the sparse
section id. When VMEMMAP is enabled, those bits are returned, cpupid
doesn't need its own member, and the page passes the VMEMMAP check.

Restrict that check to the situation it was meant to check: that we
are sizing the VMEMMAP page array correctly.

Says Arnd:

    Further experiments show that the build error already existed before,
    but was only triggered with larger values of CONFIG_NR_CPU and/or
    CONFIG_NODES_SHIFT that might be used in actual configurations but
    not in randconfig builds.

    With longer CPU and node masks, I could recreate the problem with
    kernels as old as linux-4.7 when arm64 NUMA support got added.

Reported-by: Arnd Bergmann <arnd@arndb.de>
Tested-by: Arnd Bergmann <arnd@arndb.de>
Cc: stable at vger.kernel.org
Fixes: 1a2db300348b ("arm64, numa: Add NUMA support for arm64 platforms.")
Fixes: 3e1907d5bf5a ("arm64: mm: move vmemmap region right below the linear region")
Signed-off-by: Johannes Weiner <hannes@cmpxchg.org>
---
 arch/arm64/mm/init.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/arch/arm64/mm/init.c b/arch/arm64/mm/init.c
index 1b18b4722420..86d9f9d303b0 100644
--- a/arch/arm64/mm/init.c
+++ b/arch/arm64/mm/init.c
@@ -611,11 +611,13 @@ void __init mem_init(void)
 	BUILD_BUG_ON(TASK_SIZE_32			> TASK_SIZE_64);
 #endif
 
+#ifdef CONFIG_SPARSEMEM_VMEMMAP
 	/*
 	 * Make sure we chose the upper bound of sizeof(struct page)
-	 * correctly.
+	 * correctly when sizing the VMEMMAP array.
 	 */
 	BUILD_BUG_ON(sizeof(struct page) > (1 << STRUCT_PAGE_MAX_SHIFT));
+#endif
 
 	if (PAGE_SIZE >= 16384 && get_num_physpages() <= 128) {
 		extern int sysctl_overcommit_memory;
-- 
2.18.0

^ permalink raw reply related

* [PATCH 1/5] General notification queue with user mmap()'able ring buffer
From: David Howells @ 2018-07-23 15:25 UTC (permalink / raw)
  To: viro
  Cc: linux-fsdevel, dhowells, linux-kernel, raven, keyrings,
	linux-security-module
In-Reply-To: <153235954191.32640.5792167066538704794.stgit@warthog.procyon.org.uk>

Implement a misc device that implements a general notification queue as a
ring buffer that can be mmap()'d from userspace.

The way this is done is:

 (1) An application opens the device and indicates the size of the ring
     buffer that it wants to reserve in pages (this can only be set once):

	fd = open("/dev/sync_user", O_RDWR);
	ioctl(fd, IOC_WATCH_QUEUE_NR_PAGES, nr_of_pages);

 (2) The application should then map the pages that the device has
     reserved.  Each instance of the device created by open() allocates
     separate pages so that maps of different fds don't interfere with one
     another.  Multiple mmap() calls on the same fd, however, will all work
     together.

	page_size = sysconf(_SC_PAGESIZE);
	mapping_size = nr_of_pages * page_size;
	char *buf = mmap(NULL, mapping_size, PROT_READ|PROT_WRITE,
			 MAP_SHARED, fd, 0);

The ring is divided into 8-byte slots.  Entries written into the ring are
variable size and can use between 1 and 63 slots.  A special entry is
maintained in the first two slots of the ring that contains the head and tail
pointers.  This is skipped when the ring wraps round.  Note that multislot
entries, therefore, aren't allowed to be broken over the end of the ring, but
instead "skip" entries are inserted to pad out the buffer.

Each entry has a 1-slot header that describes it:

	struct watch_notification {
		__u32	type:24;
		__u32	subtype:8;
		__u32	info;
	};

The type indicates the source (eg. mount tree changes, superblock events,
keyring changes) and the subtype indicates the event type (eg. mount, unmount;
EIO, EDQUOT; link, unlink).  The info field indicates a number of things,
including the entry length, an ID assigned to a watchpoint contributing to
this buffer, type-specific flags and meta flags, such as an overrun indicator.

Supplementary data, such as the key ID that generated an event, are attached
in additional slots.

Signed-off-by: David Howells <dhowells@redhat.com>
---

 Documentation/watch_queue.rst    |  305 ++++++++++++++
 drivers/misc/Kconfig             |    9 
 drivers/misc/Makefile            |    1 
 drivers/misc/watch_queue.c       |  835 ++++++++++++++++++++++++++++++++++++++
 include/linux/watch_queue.h      |   87 ++++
 include/uapi/linux/watch_queue.h |   81 ++++
 mm/interval_tree.c               |    2 
 mm/memory.c                      |    1 
 8 files changed, 1321 insertions(+)
 create mode 100644 Documentation/watch_queue.rst
 create mode 100644 drivers/misc/watch_queue.c
 create mode 100644 include/linux/watch_queue.h
 create mode 100644 include/uapi/linux/watch_queue.h

diff --git a/Documentation/watch_queue.rst b/Documentation/watch_queue.rst
new file mode 100644
index 000000000000..e7dc1007976d
--- /dev/null
+++ b/Documentation/watch_queue.rst
@@ -0,0 +1,305 @@
+============================
+Mappable notifications queue
+============================
+
+This is a misc device that acts as a mapped ring buffer by which userspace can
+receive notifications from the kernel.  This is can be used in conjunction
+with::
+
+  * Key/keyring notifications
+
+  * Mount topology change notifications
+
+  * Superblock event notifications
+
+
+The notifications buffers can be enabled by:
+
+	"Device Drivers"/"Misc devices"/"Mappable notification queue"
+	(CONFIG_WATCH_QUEUE)
+
+This document has the following sections:
+
+.. contents:: :local:
+
+
+Overview
+========
+
+This facility appears as a misc device file that is opened and then mapped and
+polled.  Each time it is opened, it creates a new buffer specific to the
+returned file descriptor.  Then, when the opening process sets watches, it
+indicates that particular buffer it wants notifications from that watch to be
+written into.  Note that there are no read() and write() methods (except for
+debugging).  The user is expected to access the ring directly and to use poll
+to wait for new data.
+
+If a watch is in place, notifications are only written into the buffer if the
+filter criteria are passed and if there's sufficient space available in the
+ring.  If neither of those is so, a notification will be discarded.  In the
+latter case, an overrun indicator will also be set.
+
+Note that when producing a notification, the kernel does not wait for the
+consumers to collect it, but rather just continues on.  This means that
+notifications can be generated whilst spinlocks are held and also protects the
+kernel from being held up indefinitely by a userspace malfunction.
+
+As far as the ring goes, the head index belongs to the kernel and the tail
+index belongs to userspace.  The kernel will refuse to write anything if the
+tail index becomes invalid.  Userspace *must* use appropriate memory barriers
+between reading or updating the tail index and reading the ring.
+
+
+Record Structure
+================
+
+Notification records in the ring may occupy a variable number of slots within
+the buffer, beginning with a 1-slot header::
+
+	struct watch_notification {
+		__u16	type;
+		__u16	subtype;
+		__u32	info;
+	};
+
+"type" indicates the source of the notification record and "subtype" indicates
+the type of record from that source (see the Watch Sources section below).  The
+type may also be "WATCH_TYPE_META".  This is a special record type generated
+internally by the watch queue driver itself.  There are two subtypes, one of
+which indicates records that should be just skipped (padding or metadata):
+
+    * watch_meta_skip_notification
+    * watch_meta_removal_notification
+
+The former indicates a record that should just be skipped and the latter
+indicates that an object on which a watchpoint was installed was removed or
+destroyed.
+
+"info" indicates a bunch of things, including:
+
+  * The length of the record (mask with WATCH_INFO_LENGTH).  This indicates the
+    size of the record, which may be between 1 and 63 slots.  Note that this is
+    placed appropriately within the info value so that no shifting is required
+    to convert number of occupied slots to byte length.
+
+  * The watchpoint ID (mask with WATCH_INFO_ID).  This indicates that caller's
+    ID of the watchpoint, which may be between 0 and 255.  Multiple watchpoints
+    may share a queue, and this provides a means to distinguish them.
+
+  * A buffer overrun flag (WATCH_INFO_OVERRUN flag).  If this is set in a
+    notification record, some of the preceding records were discarded.
+
+  * An ENOMEM-loss flag (WATCH_INFO_ENOMEM flag).  This is set to indicate that
+    an event was lost to ENOMEM.
+
+  * A recursive-change flag (WATCH_INFO_RECURSIVE flag).  This is set to
+    indicate that the change that happened was recursive - for instance
+    changing the attributes on an entire mount subtree.
+
+  * An exact-match flag (WATCH_INFO_IN_SUBTREE flag).  This is set if the event
+    didn't happen exactly at the watchpoint, but rather somewhere in the
+    subtree thereunder.
+
+  * Some type-specific flags (WATCH_INFO_TYPE_FLAGS).  These are set by the
+    notification producer to indicate some meaning to the kernel.
+
+Everything in info apart from the length can be used for filtering.
+
+
+Ring Structure
+==============
+
+The ring is divided into 8-byte slots.  The caller uses an ioctl() to set the
+size of the ring after opening and this must be a power-of-2 multiple of the
+system page size (so that the mask can be used with AND).
+
+The head and tail indices are stored in the first two slots in the ring, which
+are marked out as a skippable entry::
+
+	struct watch_queue_buffer {
+		union {
+			struct {
+				struct watch_notification watch;
+				volatile __u32	head;
+				volatile __u32	tail;
+				__u32		mask;
+			} meta;
+			struct watch_notification slots[0];
+		};
+	};
+
+In "meta.watch", type will be set to WATCH_TYPE_META and subtype to
+watch_meta_skip_notification so that anyone processing the buffer will just
+skip this record.  Also, because this record is here, records cannot wrap round
+the end of the buffer, so a skippable padding element will be inserted at the
+end of the buffer if needed.  Thus the contents of a notification record in the
+buffer are always contiguous.
+
+"meta.mask" is an AND'able mask to turn the index counters into slots array
+indices.
+
+The buffer is empty if "meta.head" == "meta.tail".
+
+[!] NOTE that the ring indices "meta.head" and "meta.tail" are indices into
+"slots[]" not byte offsets into the buffer.
+
+[!] NOTE that userspace must never change the head pointer.  This belongs to
+the kernel and will be updated by that.  The kernel will never change the tail
+pointer.
+
+[!] NOTE that userspace must never AND-off the tail pointer before updating it,
+but should just keep adding to it and letting it wrap naturally.  The value
+*should* be masked off when used as an index into slots[].
+
+[!] NOTE that if the distance between head and tail becomes too great, the
+kernel will assume the buffer is full and write no more until the issue is
+resolved.
+
+
+Watch Sources
+=============
+
+Any particular buffer can be fed from multiple sources.  Sources include:
+
+  * WATCH_TYPE_MOUNT_NOTIFY
+
+    Notifications of this type indicate mount tree topology changes and mount
+    attribute changes.  A watchpoint can be set on a particular file or
+    directory and notifications from the path subtree rooted at that point will
+    be intercepted.
+
+  * WATCH_TYPE_SB_NOTIFY
+
+    Notifications of this type indicate superblock events, such as quota limits
+    being hit, I/O errors being produced or network server loss/reconnection.
+    Watchpoints of this type are set directly on superblocks.
+
+  * WATCH_TYPE_KEY_NOTIFY
+
+    Notifications of this type indicate changes to keys and keyrings, including
+    the changes of keyring contents or the attributes of keys.
+
+    See Documentation/security/keys/core.rst for more information.
+
+
+Configuring Watchpoints
+=======================
+
+When a watchpoint is set up, the caller assigns an ID and can set filtering
+parameters.  The following structure is filled out and passed to the
+watchpoint creation system call::
+
+	struct watch_notification_filter {
+		__u64	subtype_filter[4];
+		__u32	info_filter;
+		__u32	info_mask;
+		__u32	info_id;
+		__u32	__reserved;
+	};
+
+"subtype_filter" is a bitmask indicating the subtypes that are of interest.  In
+this version of the structure, only the first 256 subtypes are supported.  Bit
+0 of subtype_filter[0] corresponds to subtype 0, bit 1 to subtype 1, and so on.
+
+"info_filter" and "info_mask" act as a filter on the info field of the
+notification record.  The notification is only written into the buffer if::
+
+	(watch.info & info_mask) == info_filter
+
+This can be used, for example, to ignore events that are not exactly on the
+watched point in a mount tree by specifying WATCH_INFO_IN_SUBTREE must be 0.
+
+"info_id" is OR'd into watch.info.  This indicates the watchpoint ID in the top
+8 bits.  All bits outside of WATCH_INFO_ID must be 0.
+
+"__reserved" must be 0.
+
+If the pointer to this structure is NULL, this indicates to the system call
+that the watchpoint should be removed.
+
+
+Polling
+=======
+
+The file descriptor that holds the buffer may be used with poll() and similar.
+POLLIN and POLLRDNORM are set if the buffer indices differ.  POLLERR is set if
+the buffer indices are further apart than the size of the buffer.  Wake-up
+events are only generated if the buffer is transitioned from an empty state.
+
+
+Example
+=======
+
+A buffer is created with something like the following::
+
+	fd = open("/dev/watch_queue", O_RDWR);
+
+	#define BUF_SIZE 4
+	ioctl(fd, IOC_WATCH_QUEUE_SET_SIZE, BUF_SIZE);
+
+	page_size = sysconf(_SC_PAGESIZE);
+	buf = mmap(NULL, BUF_SIZE * page_size,
+		   PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
+
+It can then be set to receive mount topology change notifications, keyring
+change notifications and superblock notifications::
+
+	memset(&filter, 0, sizeof(filter));
+	filter.subtype_filter[0] = ~0ULL;
+	filter.info_mask	 = WATCH_INFO_IN_SUBTREE;
+	filter.info_filter	 = 0;
+	filter.info_id		 = 0x01000000;
+
+	keyctl(KEYCTL_WATCH_KEY, KEY_SPEC_SESSION_KEYRING, fd, &filter);
+
+	mount_notify(AT_FDCWD, "/", 0, fd, &filter);
+
+	sb_notify(AT_FDCWD, "/", 0, fd, &filter);
+
+The notifications can then be consumed by something like the following::
+
+	extern void saw_mount_change(struct watch_notification *n);
+	extern void saw_key_change(struct watch_notification *n);
+
+	static int consumer(int fd, struct watch_queue_buffer *buf)
+	{
+		struct watch_notification *n;
+		struct pollfd p[1];
+		unsigned int head, tail, mask = buf->meta.mask;
+
+		for (;;) {
+			p[0].fd = fd;
+			p[0].events = POLLIN | POLLERR;
+			p[0].revents = 0;
+
+			if (poll(p, 1, -1) == -1 || p[0].revents & POLLERR)
+				goto went_wrong;
+
+			while (head = _atomic_load_acquire(buf->meta.head),
+			       tail = buf->meta.tail,
+			       tail != head
+			       ) {
+				n = &buf->slots[tail & mask];
+				if ((n->info & WATCH_INFO_LENGTH) == 0)
+					goto went_wrong;
+
+				switch (n->type) {
+				case WATCH_TYPE_MOUNT_NOTIFY:
+					saw_mount_change(n);
+					break;
+				case WATCH_TYPE_KEY_NOTIFY:
+					saw_key_change(n);
+					break;
+				}
+
+				tail += (n->info & WATCH_INFO_LENGTH) >> WATCH_LENGTH_SHIFT;
+				_atomic_store_release(buf->meta.tail, tail);
+			}
+		}
+
+	went_wrong:
+		return 0;
+	}
+
+Note the memory barriers when loading the head pointer and storing the tail
+pointer!
diff --git a/drivers/misc/Kconfig b/drivers/misc/Kconfig
index 3726eacdf65d..fa381a8620a7 100644
--- a/drivers/misc/Kconfig
+++ b/drivers/misc/Kconfig
@@ -4,6 +4,15 @@
 
 menu "Misc devices"
 
+config WATCH_QUEUE
+	bool "Mappable notification queue"
+	default n
+	help
+	  This is a general notification queue for the kernel to pass events to
+	  userspace through a mmap()'able ring buffer.  It can be used in
+	  conjunction with watches for mount topology change notifications,
+	  superblock change notifications and key/keyring change notifications.
+
 config SENSORS_LIS3LV02D
 	tristate
 	depends on INPUT
diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index af22bbc3d00c..823f2f99b183 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -3,6 +3,7 @@
 # Makefile for misc devices that really don't fit anywhere else.
 #
 
+obj-$(CONFIG_WATCH_QUEUE)	+= watch_queue.o
 obj-$(CONFIG_IBM_ASM)		+= ibmasm/
 obj-$(CONFIG_IBMVMC)		+= ibmvmc.o
 obj-$(CONFIG_AD525X_DPOT)	+= ad525x_dpot.o
diff --git a/drivers/misc/watch_queue.c b/drivers/misc/watch_queue.c
new file mode 100644
index 000000000000..4e4d9262622d
--- /dev/null
+++ b/drivers/misc/watch_queue.c
@@ -0,0 +1,835 @@
+/* User-mappable watch queue
+ *
+ * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#define pr_fmt(fmt) "[%05u] watchq: " fmt, current->pid
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/printk.h>
+#include <linux/miscdevice.h>
+#include <linux/fs.h>
+#include <linux/mm.h>
+#include <linux/pagemap.h>
+#include <linux/poll.h>
+#include <linux/uaccess.h>
+#include <linux/vmalloc.h>
+#include <linux/file.h>
+#include <linux/watch_queue.h>
+
+#define DEBUG_WITH_WRITE /* Allow use of write() to record notifications */
+
+MODULE_DESCRIPTION("Watch queue");
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL");
+
+struct watch_type_filter {
+	enum watch_notification_type type;
+	__u32		subtype_filter[1];	/* Bitmask of subtypes to filter on */
+	__u32		info_filter;		/* Filter on watch_notification::info */
+	__u32		info_mask;		/* Mask of relevant bits in info_filter */
+};
+
+struct watch_filter {
+	union {
+		struct rcu_head	rcu;
+		unsigned long	type_filter[2];	/* Bitmask of accepted types */
+	};
+	u32		nr_filters;		/* Number of filters */
+	struct watch_type_filter filters[];
+};
+
+struct watch_queue {
+	struct rcu_head		rcu;
+	struct address_space	mapping;
+	struct watch_filter	*filter;
+	wait_queue_head_t	waiters;
+	struct hlist_head	watches;	/* Contributory watches */
+	refcount_t		usage;
+	spinlock_t		lock;
+	bool			defunct;	/* T when queues closed */
+	u8			nr_pages;	/* Size of pages[] */
+	u8			flag_next;	/* Flag to apply to next item */
+#ifdef DEBUG_WITH_WRITE
+	u8			debug;
+#endif
+	u32			size;
+	struct watch_queue_buffer *buffer;	/* Pointer to first record */
+
+	/* The mappable pages.  The zeroth page holds the ring pointers. */
+	struct page		**pages;
+};
+
+/**
+ * post_one_notification - Post an event notification to one queue
+ * @wqueue: The watch queue to add the event to.
+ * @n: The notification record to post.
+ *
+ * Post a notification of an event into an mmap'd queue and let the user know.
+ * Returns true if successful and false on failure (eg. buffer overrun or
+ * userspace mucked up the ring indices).
+ *
+ *
+ * The size of the notification should be set in n->flags & WATCH_LENGTH and
+ * should be in units of sizeof(*n).
+ */
+static bool post_one_notification(struct watch_queue *wqueue,
+				  struct watch_notification *n)
+{
+	struct watch_queue_buffer *buf = wqueue->buffer;
+	unsigned int metalen = sizeof(buf->meta) / sizeof(buf->slots[0]);
+	unsigned int size = wqueue->size, mask = size - 1;
+	unsigned int len;
+	unsigned int ring_tail, tail, head, used, segment, h;
+
+	if (!buf)
+		return false;
+
+	len = (n->info & WATCH_INFO_LENGTH) >> WATCH_LENGTH_SHIFT;
+	if (len == 0)
+		return false;
+
+	spin_lock(&wqueue->lock); /* Protect head pointer */
+
+	if (wqueue->defunct)
+		goto out;
+
+	ring_tail = READ_ONCE(buf->meta.tail);
+	head = READ_ONCE(buf->meta.head);
+	used = head - ring_tail;
+
+	/* Check to see if userspace mucked up the pointers */
+	if (used >= size)
+		goto overrun;
+	tail = ring_tail & mask;
+	if (tail > 0 && tail < metalen)
+		goto overrun;
+
+	h = head & mask;
+	if (h >= tail) {
+		/* Head is at or after tail in the buffer.  There may then be
+		 * two segments: one to the end of buffer and one at the
+		 * beginning of the buffer between the metadata block and the
+		 * tail pointer.
+		 */
+		segment = size - h;
+		if (len > segment) {
+			/* Not enough space in the post-head segment; we need
+			 * to wrap.  When wrapping, we will have to skip the
+			 * metadata at the beginning of the buffer.
+			 */
+			if (len > tail - metalen)
+				goto overrun;
+
+			/* Fill the space at the end of the page */
+			buf->slots[h].type	= WATCH_TYPE_META;
+			buf->slots[h].subtype	= watch_meta_skip_notification;
+			buf->slots[h].info	= segment << WATCH_LENGTH_SHIFT;
+			head += segment;
+			h = 0;
+			if (h >= tail)
+				goto overrun;
+		}
+	}
+
+	if (h == 0) {
+		/* Reset and skip the header metadata */
+		buf->meta.watch.type = WATCH_TYPE_META;
+		buf->meta.watch.subtype = watch_meta_skip_notification;
+		buf->meta.watch.info = metalen << WATCH_LENGTH_SHIFT;
+		head += metalen;
+		h = metalen;
+		if (h >= tail)
+			goto overrun;
+	}
+
+	if (h < tail) {
+		/* Head is before tail in the buffer.  There may be one segment
+		 * between the two, but we may need to skip the metadata block.
+		 */
+		segment = tail - h;
+		if (len > segment)
+			goto overrun;
+	}
+
+	n->info |= wqueue->flag_next;
+	wqueue->flag_next = 0;
+	memcpy(buf->slots + h, n, len * sizeof(buf->slots[0]));
+	head += len;
+
+	smp_store_release(&buf->meta.head, head);
+	spin_unlock(&wqueue->lock);
+	if (used == 0)
+		wake_up(&wqueue->waiters);
+	return true;
+
+overrun:
+	wqueue->flag_next = WATCH_INFO_OVERRUN;
+out:
+	spin_unlock(&wqueue->lock);
+	return false;
+}
+
+/*
+ * Apply filter rules to a notification.
+ */
+static bool filter_watch_notification(const struct watch_filter *wf,
+				      const struct watch_notification *n)
+{
+	const struct watch_type_filter *wt;
+	int i;
+
+	if (!test_bit(n->type, wf->type_filter))
+		return false;
+
+	for (i = 0; i < wf->nr_filters; i++) {
+		wt = &wf->filters[i];
+		if (n->type == wt->type &&
+		    ((1U << n->subtype) & wt->subtype_filter[0]) &&
+		    (n->info & wt->info_mask) == wt->info_filter)
+			return true;
+	}
+
+	return false; /* If there is a filter, the default is to reject. */
+}
+
+/**
+ * __post_watch_notification - Post an event notification
+ * @wlist: The watch list to post the event to.
+ * @n: The notification record to post.
+ * @id: The ID to match on the watch.
+ *
+ * Post a notification of an event into a set of watch queues and let the users
+ * know.
+ *
+ * If @n is NULL then WATCH_INFO_LENGTH will be set on the next event posted.
+ *
+ * The size of the notification should be set in n->info & WATCH_INFO_LENGTH and
+ * should be in units of sizeof(*n).
+ */
+void __post_watch_notification(struct watch_list *wlist,
+			       struct watch_notification *n,
+			       u64 id)
+{
+	const struct watch_filter *wf;
+	struct watch_queue *wqueue;
+	struct watch *watch;
+
+	rcu_read_lock();
+
+	hlist_for_each_entry_rcu(watch, &wlist->watchers, list_node) {
+		if (watch->id != id)
+			continue;
+		n->info &= ~(WATCH_INFO_ID | WATCH_INFO_OVERRUN);
+		n->info |= watch->info_id;
+
+		wqueue = rcu_dereference(watch->queue);
+		wf = rcu_dereference(wqueue->filter);
+		if (wf && !filter_watch_notification(wf, n))
+			continue;
+
+		post_one_notification(wqueue, n);
+	}
+
+	rcu_read_unlock();
+}
+EXPORT_SYMBOL(__post_watch_notification);
+
+/*
+ * Allow the queue to be polled.
+ */
+static unsigned int watch_queue_poll(struct file *file, poll_table *wait)
+{
+	struct watch_queue *wqueue = file->private_data;
+	struct watch_queue_buffer *buf = wqueue->buffer;
+	unsigned int mask = 0, head, tail;
+
+	poll_wait(file, &wqueue->waiters, wait);
+
+	head = READ_ONCE(buf->meta.head);
+	tail = READ_ONCE(buf->meta.tail);
+	if (head != tail)
+		mask |= POLLIN | POLLRDNORM;
+	if (head - tail > wqueue->size)
+		mask |= POLLERR;
+	return mask;
+}
+
+static int watch_queue_set_page_dirty(struct page *page)
+{
+	SetPageDirty(page);
+	return 0;
+}
+
+static const struct address_space_operations watch_queue_aops = {
+	.set_page_dirty	= watch_queue_set_page_dirty,
+};
+
+static int watch_queue_fault(struct vm_fault *vmf)
+{
+	struct watch_queue *wqueue = vmf->vma->vm_file->private_data;
+	struct page *page;
+
+	page = wqueue->pages[vmf->pgoff];
+	get_page(page);
+	if (!lock_page_or_retry(page, vmf->vma->vm_mm, vmf->flags)) {
+		put_page(page);
+		return VM_FAULT_RETRY;
+	}
+	vmf->page = page;
+	return VM_FAULT_LOCKED;
+}
+
+static void watch_queue_map_pages(struct vm_fault *vmf,
+				  pgoff_t start_pgoff, pgoff_t end_pgoff)
+{
+	struct watch_queue *wqueue = vmf->vma->vm_file->private_data;
+	struct page *page;
+
+	rcu_read_lock();
+
+	do {
+		page = wqueue->pages[start_pgoff];
+		if (trylock_page(page)) {
+			int ret;
+			get_page(page);
+			ret = alloc_set_pte(vmf, NULL, page);
+			if (ret != 0)
+				put_page(page);
+
+			unlock_page(page);
+		}
+	} while (++start_pgoff < end_pgoff);
+
+	rcu_read_unlock();
+}
+
+static const struct vm_operations_struct watch_queue_vm_ops = {
+	.fault		= watch_queue_fault,
+	.map_pages	= watch_queue_map_pages,
+};
+
+/*
+ * Map the buffer.
+ */
+static int watch_queue_mmap(struct file *file, struct vm_area_struct *vma)
+{
+	struct watch_queue *wqueue = file->private_data;
+
+	if (vma->vm_pgoff != 0 ||
+	    vma->vm_end - vma->vm_start > wqueue->nr_pages * PAGE_SIZE ||
+	    !(pgprot_val(vma->vm_page_prot) & pgprot_val(PAGE_SHARED)))
+		return -EINVAL;
+
+	vma->vm_ops = &watch_queue_vm_ops;
+
+	vma_interval_tree_insert(vma, &wqueue->mapping.i_mmap);
+	return 0;
+}
+
+/*
+ * Allocate the required number of pages.
+ */
+static long watch_queue_set_size(struct watch_queue *wqueue, unsigned long nr_pages)
+{
+	struct watch_queue_buffer *buf;
+	u32 len;
+	int i;
+
+	if (nr_pages == 0 ||
+	    nr_pages > 16 || /* TODO: choose a better hard limit */
+	    !is_power_of_2(nr_pages))
+		return -EINVAL;
+
+	wqueue->pages = kcalloc(nr_pages, sizeof(struct page *), GFP_KERNEL);
+	if (!wqueue->pages)
+		goto err;
+
+	for (i = 0; i < nr_pages; i++) {
+		wqueue->pages[i] = alloc_page(GFP_KERNEL | __GFP_ZERO);
+		if (!wqueue->pages[i])
+			goto err_some_pages;
+		wqueue->pages[i]->mapping = &wqueue->mapping;
+		SetPageUptodate(wqueue->pages[i]);
+	}
+
+	buf = vmap(wqueue->pages, nr_pages, VM_MAP, PAGE_SHARED);
+	if (!buf)
+		goto err_some_pages;
+
+	wqueue->buffer = buf;
+	wqueue->nr_pages = nr_pages;
+	wqueue->size = ((nr_pages * PAGE_SIZE) / sizeof(struct watch_notification));
+
+	/* The first four slots in the buffer contain metadata about the ring,
+	 * including the head and tail indices and mask.
+	 */
+	len = sizeof(buf->meta) / sizeof(buf->slots[0]);
+	buf->meta.watch.info	= len << WATCH_LENGTH_SHIFT;
+	buf->meta.watch.type	= WATCH_TYPE_META;
+	buf->meta.watch.subtype	= watch_meta_skip_notification;
+	buf->meta.tail		= len;
+	buf->meta.mask		= wqueue->size - 1;
+	smp_store_release(&buf->meta.head, len);
+	return 0;
+
+err_some_pages:
+	for (i--; i >= 0; i--) {
+		ClearPageUptodate(wqueue->pages[i]);
+		wqueue->pages[i]->mapping = NULL;
+		put_page(wqueue->pages[i]);
+	}
+
+	kfree(wqueue->pages);
+	wqueue->pages = NULL;
+err:
+	return -ENOMEM;
+}
+
+/*
+ * Set the filter on a watch queue.
+ */
+static long watch_queue_set_filter(struct watch_queue *wqueue,
+				   struct watch_notification_filter __user *_filter)
+{
+	struct watch_notification_type_filter *tf;
+	struct watch_notification_filter filter;
+	struct watch_type_filter *q;
+	struct watch_filter *old, *wfilter;
+	int ret, nr_filter = 0, i;
+
+	if (!_filter) {
+		/* Remove the old filter */
+		wfilter = NULL;
+		goto set;
+	}
+
+	/* Grab the user's filter specification */
+	if (copy_from_user(&filter, _filter, sizeof(filter)) != 0)
+		return -EFAULT;
+	if (filter.nr_filters == 0 ||
+	    filter.nr_filters > 16 ||
+	    filter.__reserved != 0)
+		return -EINVAL;
+
+	tf = memdup_user(_filter->filters, filter.nr_filters * sizeof(*tf));
+	if (IS_ERR(tf))
+		return PTR_ERR(tf);
+
+	ret = -EINVAL;
+	for (i = 0; i < filter.nr_filters; i++) {
+		if ((tf[i].info_filter & ~tf[i].info_mask) ||
+		    tf[i].info_mask & WATCH_INFO_LENGTH)
+			goto err_filter;
+		/* Ignore any unknown types */
+		if (tf[i].type >= sizeof(wfilter->type_filter) * 8)
+			continue;
+		nr_filter++;
+	}
+
+	/* Now we need to build the internal filter from only the
+	 * relevant user-specified filters.
+	 */
+	ret = -ENOMEM;
+	wfilter = kzalloc(sizeof(*wfilter) + nr_filter * sizeof(*q), GFP_KERNEL);
+	if (!wfilter)
+		goto err_filter;
+	wfilter->nr_filters = nr_filter;
+
+	q = wfilter->filters;
+	for (i = 0; i < filter.nr_filters; i++) {
+		if (tf[i].type >= sizeof(wfilter->type_filter) * BITS_PER_LONG)
+			continue;
+
+		q->type			= tf[i].type;
+		q->info_filter		= tf[i].info_filter;
+		q->info_mask		= tf[i].info_mask;
+		q->subtype_filter[0]	= tf[i].subtype_filter[0];
+		__set_bit(q->type, wfilter->type_filter);
+		q++;
+	}
+
+	kfree(tf);
+set:
+	rcu_swap_protected(wqueue->filter, wfilter, true /* inode lock */);
+	if (old)
+		kfree_rcu(wfilter, rcu);
+	return 0;
+
+err_filter:
+	kfree(tf);
+	return ret;
+}
+
+/*
+ * Set parameters.
+ */
+static long watch_queue_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
+{
+	struct watch_queue *wqueue = file->private_data;
+	long ret;
+
+	switch (cmd) {
+	case IOC_WATCH_QUEUE_SET_SIZE:
+		if (wqueue->buffer)
+			return -EBUSY;
+		inode_lock(file_inode(file));
+		ret = watch_queue_set_size(wqueue, arg);
+		inode_unlock(file_inode(file));
+		return ret;
+
+	case IOC_WATCH_QUEUE_SET_FILTER:
+		inode_lock(file_inode(file));
+		ret = watch_queue_set_filter(
+			wqueue, (struct watch_notification_filter __user *)arg);
+		inode_unlock(file_inode(file));
+		return ret;
+
+	default:
+		return -EOPNOTSUPP;
+	}
+}
+
+/*
+ * Open the file.
+ */
+static int watch_queue_open(struct inode *inode, struct file *file)
+{
+	struct watch_queue *wqueue;
+
+	wqueue = kzalloc(sizeof(*wqueue), GFP_KERNEL);
+	if (!wqueue)
+		return -ENOMEM;
+
+	wqueue->mapping.a_ops = &watch_queue_aops;
+	wqueue->mapping.i_mmap = RB_ROOT_CACHED;
+	init_rwsem(&wqueue->mapping.i_mmap_rwsem);
+	spin_lock_init(&wqueue->mapping.private_lock);
+
+	refcount_set(&wqueue->usage, 1);
+	spin_lock_init(&wqueue->lock);
+	init_waitqueue_head(&wqueue->waiters);
+
+	file->private_data = wqueue;
+	return 0;
+}
+
+/**
+ * put_watch_queue - Dispose of a ref on a watchqueue.
+ * @wq: The watch queue to unref.
+ */
+void put_watch_queue(struct watch_queue *wqueue)
+{
+	if (refcount_dec_and_test(&wqueue->usage))
+		kfree_rcu(wqueue, rcu);
+}
+EXPORT_SYMBOL(put_watch_queue);
+
+/*
+ * Discard a watch.
+ */
+static void put_watch(struct watch *watch)
+{
+	if (refcount_dec_and_test(&watch->usage)) {
+		put_watch_queue(watch->queue);
+		kfree_rcu(watch, rcu);
+	}
+}
+
+/**
+ * add_watch_to_object - Add a watch on an object
+ * @watch: The watch to add
+ *
+ * @watch->queue and @watch->watch_list must have been set to point to the
+ * queue to post notifications to and the watch list of the object to be
+ * watched.
+ *
+ * The caller must pin the queue and the list both and must hold the list
+ * locked against racing watch additions/removals.
+ */
+int add_watch_to_object(struct watch *watch)
+{
+	struct watch_queue *wqueue = watch->queue;
+	struct watch_list *wlist = watch->watch_list;
+	struct watch *w;
+
+	hlist_for_each_entry(w, &wlist->watchers, list_node) {
+		if (watch->id == w->id)
+			return -EBUSY;
+	}
+
+	spin_lock(&wqueue->lock);
+	refcount_inc(&wqueue->usage);
+	hlist_add_head(&watch->queue_node, &wqueue->watches);
+	spin_unlock(&wqueue->lock);
+
+	hlist_add_head(&watch->list_node, &wlist->watchers);
+	return 0;
+}
+EXPORT_SYMBOL(add_watch_to_object);
+
+/**
+ * remove_watch_from_object - Remove a watch or all watches from an object.
+ * @wlist: The watch list to remove from
+ * @wq: The watch queue of interest (ignored if @all is true)
+ * @id: The ID of the watch to remove (ignored if @all is true)
+ * @all: True to remove all objects
+ *
+ * Remove a specific watch or all watches from an object.  A notification is
+ * sent to the watcher to tell them that this happened.
+ */
+int remove_watch_from_object(struct watch_list *wlist, struct watch_queue *wq,
+			     u64 id, bool all)
+{
+	struct watch_notification n;
+	struct watch_queue *wqueue;
+	struct watch *watch;
+	int ret = -EBADSLT;
+
+	rcu_read_lock();
+
+again:
+	spin_lock(&wlist->lock);
+	hlist_for_each_entry(watch, &wlist->watchers, list_node) {
+		if (all || (watch->id == id && watch->queue == wq))
+			goto found;
+	}
+	spin_unlock(&wlist->lock);
+	goto out;
+
+found:
+	ret = 0;
+	hlist_del_init_rcu(&watch->list_node);
+	rcu_assign_pointer(watch->watch_list, NULL);
+	spin_unlock(&wlist->lock);
+
+	n.type = WATCH_TYPE_META;
+	n.subtype = watch_meta_removal_notification;
+	n.info = watch->info_id | sizeof(n);
+
+	post_one_notification(watch->queue, &n);
+
+	/* We don't need the watch list lock for the next bit as RCU is
+	 * protecting everything from being deallocated.
+	 */
+	wqueue = rcu_dereference(watch->queue);
+	if (wqueue) {
+		spin_lock(&wqueue->lock);
+
+		if (!hlist_unhashed(&watch->queue_node)) {
+			hlist_del_init_rcu(&watch->queue_node);
+			put_watch(watch);
+		}
+
+		spin_unlock(&wqueue->lock);
+	}
+
+	if (wlist->release_watch) {
+		rcu_read_unlock();
+		wlist->release_watch(wlist, watch);
+		rcu_read_lock();
+	}
+	put_watch(watch);
+
+	if (all && !hlist_empty(&wlist->watchers))
+		goto again;
+out:
+	rcu_read_unlock();
+	return ret;
+}
+EXPORT_SYMBOL(remove_watch_from_object);
+
+/*
+ * Remove all the watches that are contributory to a queue.  This will
+ * potentially race with removal of the watches by the destruction of the
+ * objects being watched or the distribution of notifications.
+ */
+static void watch_queue_clear(struct watch_queue *wqueue)
+{
+	struct watch_list *wlist;
+	struct watch *watch;
+	bool release;
+
+	rcu_read_lock();
+	spin_lock(&wqueue->lock);
+
+	/* Prevent new additions and prevent notifications from happening */
+	wqueue->defunct = true;
+
+	while (!hlist_empty(&wqueue->watches)) {
+		watch = hlist_entry(wqueue->watches.first, struct watch, queue_node);
+		hlist_del_init_rcu(&watch->queue_node);
+		spin_unlock(&wqueue->lock);
+
+		/* We can't do the next bit under the queue lock as we need to
+		 * get the list lock - which would cause a deadlock if someone
+		 * was removing from the opposite direction at the same time or
+		 * posting a notification.
+		 */
+		wlist = rcu_dereference(watch->watch_list);
+		if (wlist) {
+			spin_lock(&wlist->lock);
+
+			release = !hlist_unhashed(&watch->list_node);
+			if (release) {
+				hlist_del_init_rcu(&watch->list_node);
+				rcu_assign_pointer(watch->watch_list, NULL);
+			}
+
+			spin_unlock(&wlist->lock);
+
+			if (release) {
+				if (wlist->release_watch) {
+					rcu_read_unlock();
+					/* This might need to call dput(), so
+					 * we have to drop all the locks.
+					 */
+					wlist->release_watch(wlist, watch);
+					rcu_read_lock();
+				}
+				put_watch(watch);
+			}
+		}
+
+		put_watch(watch);
+		spin_lock(&wqueue->lock);
+	}
+
+	spin_unlock(&wqueue->lock);
+	rcu_read_unlock();
+}
+
+/*
+ * Release the file.
+ */
+static int watch_queue_release(struct inode *inode, struct file *file)
+{
+	struct watch_queue *wqueue = file->private_data;
+	int i, pgref;
+
+	watch_queue_clear(wqueue);
+
+	if (wqueue->pages && wqueue->pages[0])
+		WARN_ON(page_ref_count(wqueue->pages[0]) != 1);
+
+	if (wqueue->buffer)
+		vfree(wqueue->buffer);
+	for (i = 0; i < wqueue->nr_pages; i++) {
+		ClearPageUptodate(wqueue->pages[i]);
+		wqueue->pages[i]->mapping = NULL;
+		pgref = page_ref_count(wqueue->pages[i]);
+		WARN(pgref != 1,
+		     "FREE PAGE[%d] refcount %d\n", i, page_ref_count(wqueue->pages[i]));
+		__free_page(wqueue->pages[i]);
+	}
+	if (wqueue->filter)
+		kfree_rcu(wqueue->filter, rcu);
+	kfree(wqueue->pages);
+	put_watch_queue(wqueue);
+	return 0;
+}
+
+#ifdef DEBUG_WITH_WRITE
+static ssize_t watch_queue_write(struct file *file,
+				 const char __user *_buf, size_t len, loff_t *pos)
+{
+	struct watch_notification *n;
+	struct watch_queue *wqueue = file->private_data;
+	ssize_t ret;
+
+	if (!wqueue->buffer)
+		return -ENOBUFS;
+
+	if (len & ~WATCH_INFO_LENGTH || len == 0 || !_buf)
+		return -EINVAL;
+
+	n = memdup_user(_buf, len);
+	if (IS_ERR(n))
+		return PTR_ERR(n);
+
+	ret = -EINVAL;
+	if ((n->info & WATCH_INFO_LENGTH) != len)
+		goto error;
+	n->info &= (WATCH_INFO_LENGTH | WATCH_INFO_TYPE_FLAGS | WATCH_INFO_ID);
+
+	if (post_one_notification(wqueue, n))
+		wqueue->debug = 0;
+	else
+		wqueue->debug++;
+	ret = len;
+	if (wqueue->debug > 20)
+		ret = -EIO;
+
+error:
+	kfree(n);
+	return ret;
+}
+#endif
+
+static const struct file_operations watch_queue_fops = {
+	.owner		= THIS_MODULE,
+	.open		= watch_queue_open,
+	.release	= watch_queue_release,
+	.unlocked_ioctl	= watch_queue_ioctl,
+	.poll		= watch_queue_poll,
+	.mmap		= watch_queue_mmap,
+#ifdef DEBUG_WITH_WRITE
+	.write		= watch_queue_write,
+#endif
+	.llseek		= no_llseek,
+};
+
+/**
+ * get_watch_queue - Get a watch queue from its file descriptor.
+ * @fd: The fd to query.
+ */
+struct watch_queue *get_watch_queue(int fd)
+{
+	struct watch_queue *wqueue = ERR_PTR(-EBADF);
+	struct fd f;
+
+	f = fdget(fd);
+	if (f.file) {
+		wqueue = ERR_PTR(-EINVAL);
+		if (f.file->f_op == &watch_queue_fops) {
+			wqueue = f.file->private_data;
+			refcount_inc(&wqueue->usage);
+		}
+		fdput(f);
+	}
+
+	return wqueue;
+}
+EXPORT_SYMBOL(get_watch_queue);
+
+static struct miscdevice watch_queue_dev = {
+	.minor	= MISC_DYNAMIC_MINOR,
+	.name	= "watch_queue",
+	.fops	= &watch_queue_fops,
+	.mode	= 0666,
+};
+
+static int __init watch_queue_init(void)
+{
+	int ret;
+
+	ret = misc_register(&watch_queue_dev);
+	if (ret < 0)
+		pr_err("Failed to register %d\n", ret);
+	return ret;
+}
+fs_initcall(watch_queue_init);
+
+static void __exit watch_queue_exit(void)
+{
+	misc_deregister(&watch_queue_dev);
+}
+module_exit(watch_queue_exit);
diff --git a/include/linux/watch_queue.h b/include/linux/watch_queue.h
new file mode 100644
index 000000000000..465b14f3ad42
--- /dev/null
+++ b/include/linux/watch_queue.h
@@ -0,0 +1,87 @@
+/* User-mappable watch queue
+ *
+ * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_WATCH_QUEUE_H
+#define _LINUX_WATCH_QUEUE_H
+
+#include <uapi/linux/watch_queue.h>
+
+#ifdef CONFIG_WATCH_QUEUE
+
+struct watch_queue;
+
+/*
+ * Representation of a watch on an object.
+ */
+struct watch {
+	union {
+		struct rcu_head	rcu;
+		u32		info_id;	/* ID to be OR'd in to info field */
+	};
+	struct watch_queue __rcu *queue;	/* Queue to post events to */
+	struct hlist_node	queue_node;	/* Link in queue->watches */
+	struct watch_list __rcu	*watch_list;
+	struct hlist_node	list_node;	/* Link in watch_list->watchers */
+	void			*private;	/* Private data for the watched object */
+	u64			id;		/* Internal identifier */
+	refcount_t		usage;
+};
+
+/*
+ * List of watches on an object.
+ */
+struct watch_list {
+	struct rcu_head		rcu;
+	struct hlist_head	watchers;
+	void (*release_watch)(struct watch_list *, struct watch *);
+	spinlock_t		lock;
+};
+
+extern void __post_watch_notification(struct watch_list *,
+				      struct watch_notification *, u64);
+extern struct watch_queue *get_watch_queue(int);
+extern void put_watch_queue(struct watch_queue *);
+extern void put_watch_list(struct watch_list *);
+extern int add_watch_to_object(struct watch *);
+extern int remove_watch_from_object(struct watch_list *, struct watch_queue *, u64, bool);
+
+static inline void init_watch_list(struct watch_list *wlist)
+{
+	INIT_HLIST_HEAD(&wlist->watchers);
+	spin_lock_init(&wlist->lock);
+}
+
+static inline void init_watch(struct watch *watch)
+{
+	refcount_set(&watch->usage, 1);
+	INIT_HLIST_NODE(&watch->list_node);
+	INIT_HLIST_NODE(&watch->queue_node);
+}
+
+static inline void post_watch_notification(struct watch_list *wlist,
+					   struct watch_notification *n,
+					   u64 id)
+{
+	if (unlikely(wlist))
+		__post_watch_notification(wlist, n, id);
+}
+
+static inline void remove_watch_list(struct watch_list *wlist)
+{
+	if (wlist) {
+		remove_watch_from_object(wlist, NULL, 0, true);
+		kfree_rcu(wlist, rcu);
+	}
+}
+
+#endif
+
+#endif /* _LINUX_WATCH_QUEUE_H */
diff --git a/include/uapi/linux/watch_queue.h b/include/uapi/linux/watch_queue.h
new file mode 100644
index 000000000000..98ac4d46ab0d
--- /dev/null
+++ b/include/uapi/linux/watch_queue.h
@@ -0,0 +1,81 @@
+/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */
+#ifndef _UAPI_LINUX_WATCH_QUEUE_H
+#define _UAPI_LINUX_WATCH_QUEUE_H
+
+#include <linux/types.h>
+#include <linux/ioctl.h>
+
+#define IOC_WATCH_QUEUE_SET_SIZE	_IO('s', 0x01)	/* Set the size in pages */
+#define IOC_WATCH_QUEUE_SET_FILTER	_IO('s', 0x02)	/* Set the filter */
+
+enum watch_notification_type {
+	WATCH_TYPE_META		= 0,	/* Special record */
+	WATCH_TYPE_MOUNT_NOTIFY	= 1,	/* Mount notification record */
+	WATCH_TYPE_SB_NOTIFY	= 2,	/* Superblock notification */
+	WATCH_TYPE_KEY_NOTIFY	= 3,	/* Key/keyring change notification */
+#define WATCH_TYPE___NR 4
+};
+
+enum watch_meta_notification_subtype {
+	watch_meta_skip_notification	= 0,	/* Just skip this record */
+	watch_meta_removal_notification	= 1,	/* Watched object was removed */
+};
+
+/*
+ * Notification record
+ */
+struct watch_notification {
+	__u32			type:24;	/* enum watch_notification_type */
+	__u32			subtype:8;	/* Type-specific subtype (filterable) */
+	__u32			info;
+#define WATCH_INFO_OVERRUN	0x00000001	/* Event(s) lost due to overrun */
+#define WATCH_INFO_ENOMEM	0x00000002	/* Event(s) lost due to ENOMEM */
+#define WATCH_INFO_RECURSIVE	0x00000004	/* Change was recursive */
+#define WATCH_INFO_LENGTH	0x000001f8	/* Length of record / sizeof(watch_notification) */
+#define WATCH_INFO_IN_SUBTREE	0x00000200	/* Change was not at watched root */
+#define WATCH_INFO_TYPE_FLAGS	0x00ff0000	/* Type-specific flags */
+#define WATCH_INFO_FLAG_0	0x00010000
+#define WATCH_INFO_FLAG_1	0x00020000
+#define WATCH_INFO_FLAG_2	0x00040000
+#define WATCH_INFO_FLAG_3	0x00080000
+#define WATCH_INFO_FLAG_4	0x00100000
+#define WATCH_INFO_FLAG_5	0x00200000
+#define WATCH_INFO_FLAG_6	0x00400000
+#define WATCH_INFO_FLAG_7	0x00800000
+#define WATCH_INFO_ID		0xff000000	/* ID of watchpoint */
+};
+
+#define WATCH_LENGTH_SHIFT	3
+
+struct watch_queue_buffer {
+	union {
+		/* The first few entries are special, containing the
+		 * ring management variables.
+		 */
+		struct {
+			struct watch_notification watch; /* WATCH_TYPE_SKIP */
+			volatile __u32	head;		/* Ring head index */
+			volatile __u32	tail;		/* Ring tail index */
+			__u32		mask;		/* Ring index mask */
+		} meta;
+		struct watch_notification slots[0];
+	};
+};
+
+/*
+ * Notification filtering rules (IOC_WATCH_QUEUE_SET_FILTER).
+ */
+struct watch_notification_type_filter {
+	__u32	type;			/* Type to apply filter to */
+	__u32	info_filter;		/* Filter on watch_notification::info */
+	__u32	info_mask;		/* Mask of relevant bits in info_filter */
+	__u32	subtype_filter[8];	/* Bitmask of subtypes to filter on */
+};
+
+struct watch_notification_filter {
+	__u32	nr_filters;		/* Number of filters */
+	__u32	__reserved;		/* Must be 0 */
+	struct watch_notification_type_filter filters[];
+};
+
+#endif /* _UAPI_LINUX_WATCH_QUEUE_H */
diff --git a/mm/interval_tree.c b/mm/interval_tree.c
index 27ddfd29112a..9a53ddf4bd62 100644
--- a/mm/interval_tree.c
+++ b/mm/interval_tree.c
@@ -25,6 +25,8 @@ INTERVAL_TREE_DEFINE(struct vm_area_struct, shared.rb,
 		     unsigned long, shared.rb_subtree_last,
 		     vma_start_pgoff, vma_last_pgoff,, vma_interval_tree)
 
+EXPORT_SYMBOL_GPL(vma_interval_tree_insert);
+
 /* Insert node immediately after prev in the interval tree */
 void vma_interval_tree_insert_after(struct vm_area_struct *node,
 				    struct vm_area_struct *prev,
diff --git a/mm/memory.c b/mm/memory.c
index 7206a634270b..79a2b13cc7cc 100644
--- a/mm/memory.c
+++ b/mm/memory.c
@@ -3462,6 +3462,7 @@ int alloc_set_pte(struct vm_fault *vmf, struct mem_cgroup *memcg,
 
 	return 0;
 }
+EXPORT_SYMBOL_GPL(alloc_set_pte);
 
 
 /**

^ permalink raw reply related

* [PATCH 5/5] Add sample notification program
From: David Howells @ 2018-07-23 15:26 UTC (permalink / raw)
  To: viro
  Cc: linux-fsdevel, dhowells, linux-kernel, raven, keyrings,
	linux-security-module
In-Reply-To: <153235954191.32640.5792167066538704794.stgit@warthog.procyon.org.uk>

This needs to be linked with -lkeyutils.

It is run like:

	./watch_test

and watches "/" for mount changes and the current session keyring for key
changes:

	# keyctl add user a a @s
	1035096409
	# keyctl unlink 1035096409 @s
	# mount -t tmpfs none /mnt/nfsv3tcp/
	# umount /mnt/nfsv3tcp

producing:

	# ./watch_test
	ptrs h=4 t=2 m=20003
	NOTIFY[00000004-00000002] ty=0003 sy=0002 i=01000010
	KEY 2ffc2e5d change=2[linked] aux=1035096409
	ptrs h=6 t=4 m=20003
	NOTIFY[00000006-00000004] ty=0003 sy=0003 i=01000010
	KEY 2ffc2e5d change=3[unlinked] aux=1035096409
	ptrs h=8 t=6 m=20003
	NOTIFY[00000008-00000006] ty=0001 sy=0000 i=02000010
	MOUNT 00000013 change=0[new_mount] aux=168
	ptrs h=a t=8 m=20003
	NOTIFY[0000000a-00000008] ty=0001 sy=0001 i=02000010
	MOUNT 00000013 change=1[unmount] aux=168
---

 samples/Kconfig                  |    6 +
 samples/Makefile                 |    2 
 samples/watch_queue/Makefile     |    9 +
 samples/watch_queue/watch_test.c |  232 ++++++++++++++++++++++++++++++++++++++
 4 files changed, 248 insertions(+), 1 deletion(-)
 create mode 100644 samples/watch_queue/Makefile
 create mode 100644 samples/watch_queue/watch_test.c

diff --git a/samples/Kconfig b/samples/Kconfig
index 1c5658bc6224..dac30cefe895 100644
--- a/samples/Kconfig
+++ b/samples/Kconfig
@@ -156,4 +156,10 @@ config SAMPLE_MOUNT_API
 	help
 	  Build example userspace program(s) that use the new mount API.
 
+config SAMPLE_WATCH_QUEUE
+	bool "Build example /dev/watch_queue notification consumer"
+	help
+	  Build example userspace program to use the new mount_notify(),
+	  sb_notify() syscalls and the KEYCTL_WATCH_KEY keyctl() function.
+
 endif # SAMPLES
diff --git a/samples/Makefile b/samples/Makefile
index 31d08cc71a5c..ed545cbf65c8 100644
--- a/samples/Makefile
+++ b/samples/Makefile
@@ -3,4 +3,4 @@
 obj-$(CONFIG_SAMPLES)	+= kobject/ kprobes/ trace_events/ livepatch/ \
 			   hw_breakpoint/ kfifo/ kdb/ hidraw/ rpmsg/ seccomp/ \
 			   configfs/ connector/ v4l/ trace_printk/ \
-			   vfio-mdev/ statx/ qmi/ mount_api/
+			   vfio-mdev/ statx/ qmi/ mount_api/ watch_queue/
diff --git a/samples/watch_queue/Makefile b/samples/watch_queue/Makefile
new file mode 100644
index 000000000000..1f20ee2b0add
--- /dev/null
+++ b/samples/watch_queue/Makefile
@@ -0,0 +1,9 @@
+# List of programs to build
+hostprogs-$(CONFIG_SAMPLE_WATCH_QUEUE) := watch_test
+
+# Tell kbuild to always build the programs
+always := $(hostprogs-y)
+
+HOSTCFLAGS_watch_test.o += -I$(objtree)/usr/include
+
+HOSTLOADLIBES_watch_test += -lkeyutils
diff --git a/samples/watch_queue/watch_test.c b/samples/watch_queue/watch_test.c
new file mode 100644
index 000000000000..cdbaaf5a1163
--- /dev/null
+++ b/samples/watch_queue/watch_test.c
@@ -0,0 +1,232 @@
+/* Use /dev/watch_queue to watch for keyring and mount topology changes.
+ *
+ * Copyright (C) 2018 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <stdbool.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <errno.h>
+#include <sys/wait.h>
+#include <sys/ioctl.h>
+#include <sys/mman.h>
+#include <poll.h>
+#include <limits.h>
+#include <linux/watch_queue.h>
+#include <linux/unistd.h>
+#include <linux/keyctl.h>
+
+#define BUF_SIZE 4
+
+static const char *key_subtypes[] = {
+	[notify_key_instantiated]	= "instantiated",
+	[notify_key_updated]		= "updated",
+	[notify_key_linked]		= "linked",
+	[notify_key_unlinked]		= "unlinked",
+	[notify_key_cleared]		= "cleared",
+	[notify_key_revoked]		= "revoked",
+	[notify_key_invalidated]	= "invalidated",
+	[notify_key_setattr]		= "setattr",
+};
+
+static void saw_key_change(struct watch_notification *n)
+{
+	struct key_notification *k = (struct key_notification *)n;
+	unsigned int len = n->info & WATCH_INFO_LENGTH;
+
+	if (len != sizeof(struct key_notification))
+		return;
+
+	printf("KEY %08x change=%u[%s] aux=%u\n",
+	       k->key_id, n->subtype, key_subtypes[n->subtype], k->aux);
+}
+
+static const char *mount_subtypes[] = {
+	[notify_mount_new_mount]	= "new_mount",
+	[notify_mount_unmount]		= "unmount",
+	[notify_mount_expiry]		= "expiry",
+	[notify_mount_readonly]		= "readonly",
+	[notify_mount_setattr]		= "setattr",
+	[notify_mount_move_from]	= "move_from",
+	[notify_mount_move_to]		= "move_to",
+};
+
+static long keyctl_watch_key(int key, int watch_fd, int watch_id)
+{
+	return syscall(__NR_keyctl, KEYCTL_WATCH_KEY, key, watch_fd, watch_id);
+}
+
+static void saw_mount_change(struct watch_notification *n)
+{
+	struct mount_notification *m = (struct mount_notification *)n;
+	unsigned int len = n->info & WATCH_INFO_LENGTH;
+
+	if (len != sizeof(struct mount_notification))
+		return;
+
+	printf("MOUNT %08x change=%u[%s] aux=%u\n",
+	       m->triggered_on, n->subtype, mount_subtypes[n->subtype], m->changed_mount);
+}
+
+static const char *super_subtypes[] = {
+	[notify_superblock_readonly]	= "readonly",
+	[notify_superblock_error]	= "error",
+	[notify_superblock_edquot]	= "edquot",
+	[notify_superblock_network]	= "network",
+};
+
+static void saw_super_change(struct watch_notification *n)
+{
+	struct superblock_notification *s = (struct superblock_notification *)n;
+	unsigned int len = n->info & WATCH_INFO_LENGTH;
+
+	if (len < sizeof(struct superblock_notification))
+		return;
+
+	printf("SUPER %08llx change=%u[%s]\n",
+	       s->sb_id, n->subtype, super_subtypes[n->subtype]);
+}
+
+/*
+ * Consume and display events.
+ */
+static int consumer(int fd, struct watch_queue_buffer *buf)
+{
+	struct watch_notification *n;
+	struct pollfd p[1];
+	unsigned int head, tail, mask = buf->meta.mask;
+
+	for (;;) {
+		p[0].fd = fd;
+		p[0].events = POLLIN | POLLERR;
+		p[0].revents = 0;
+
+		if (poll(p, 1, -1) == -1) {
+			perror("poll");
+			break;
+		}
+
+		printf("ptrs h=%x t=%x m=%x\n",
+		       buf->meta.head, buf->meta.tail, buf->meta.mask);
+
+		while (head = buf->meta.head,
+		       tail = buf->meta.tail,
+		       tail != head
+		       ) {
+			asm ("lfence" : : : "memory" );
+			n = &buf->slots[tail & mask];
+			printf("NOTIFY[%08x-%08x] ty=%04x sy=%04x i=%08x\n",
+			       head, tail, n->type, n->subtype, n->info);
+			if ((n->info & WATCH_INFO_LENGTH) == 0)
+				goto out;
+
+			switch (n->type) {
+			case WATCH_TYPE_META:
+				if (n->subtype == watch_meta_removal_notification)
+					printf("REMOVAL of watchpoint %08x\n",
+					       n->info & WATCH_INFO_ID);
+				break;
+			case WATCH_TYPE_MOUNT_NOTIFY:
+				saw_mount_change(n);
+				break;
+			case WATCH_TYPE_SB_NOTIFY:
+				saw_super_change(n);
+				break;
+			case WATCH_TYPE_KEY_NOTIFY:
+				saw_key_change(n);
+				break;
+			}
+
+			tail += (n->info & WATCH_INFO_LENGTH) >> WATCH_LENGTH_SHIFT;
+			asm("mfence" ::: "memory");
+			buf->meta.tail = tail;
+		}
+	}
+
+out:
+	return 0;
+}
+
+static struct watch_notification_filter filter = {
+	.nr_filters	= 3,
+	.__reserved	= 0,
+	.filters = {
+		[0] = {
+			.type			= WATCH_TYPE_MOUNT_NOTIFY,
+			// Reject move-from notifications
+			.subtype_filter[0]	= UINT_MAX & ~(1 << notify_mount_move_from),
+		},
+		[1]	= {
+			.type			= WATCH_TYPE_SB_NOTIFY,
+			// Only accept notification of changes to R/O state
+			.subtype_filter[0]	= (1 << notify_superblock_readonly),
+			// Only accept notifications of change-to-R/O
+			.info_mask		= WATCH_INFO_FLAG_0,
+			.info_filter		= WATCH_INFO_FLAG_0,
+		},
+		[2]	= {
+			.type			= WATCH_TYPE_KEY_NOTIFY,
+			.subtype_filter[0]	= UINT_MAX,
+		},
+	},
+};
+
+int main(int argc, char **argv)
+{
+	struct watch_queue_buffer *buf;
+	size_t page_size;
+	int fd;
+
+	fd = open("/dev/watch_queue", O_RDWR);
+	if (fd == -1) {
+		perror("/dev/watch_queue");
+		exit(1);
+	}
+
+	if (ioctl(fd, IOC_WATCH_QUEUE_SET_SIZE, BUF_SIZE) == -1) {
+		perror("/dev/watch_queue(size)");
+		exit(1);
+	}
+
+	if (ioctl(fd, IOC_WATCH_QUEUE_SET_FILTER, &filter) == -1) {
+		perror("/dev/watch_queue(filter)");
+		exit(1);
+	}
+
+	page_size = sysconf(_SC_PAGESIZE);
+	buf = mmap(NULL, BUF_SIZE * page_size, PROT_READ | PROT_WRITE,
+		   MAP_SHARED, fd, 0);
+	if (buf == MAP_FAILED) {
+		perror("mmap");
+		exit(1);
+	}
+
+	if (keyctl_watch_key(KEY_SPEC_SESSION_KEYRING, fd, 0x01) == -1) {
+		perror("keyctl");
+		exit(1);
+	}
+
+	if (syscall(__NR_mount_notify, AT_FDCWD, "/", 0, fd, 0x02) == -1) {
+		perror("mount_notify");
+		exit(1);
+	}
+
+	if (syscall(__NR_sb_notify, AT_FDCWD, "/mnt", 0, fd, 0x03) == -1) {
+		perror("sb_notify");
+		exit(1);
+	}
+
+	return consumer(fd, buf);
+}

^ permalink raw reply related

* Re: [PATCH resend 1/2] btrfs: allow defrag on a file opened ro that has rw permissions
From: David Sterba @ 2018-07-23 15:26 UTC (permalink / raw)
  To: Adam Borowski; +Cc: David Sterba, Mark Fasheh, linux-btrfs
In-Reply-To: <20180717220900.3588-1-kilobyte@angband.pl>

On Wed, Jul 18, 2018 at 12:08:59AM +0200, Adam Borowski wrote:
> Requiring a rw descriptor conflicts both ways with exec, returning ETXTBSY
> whenever you try to defrag a program that's currently being run, or
> causing intermittent exec failures on a live system being defragged.
> 
> As defrag doesn't change the file's contents in any way, there's no reason
> to consider it a rw operation.  Thus, let's check only whether the file
> could have been opened rw.  Such access control is still needed as
> currently defrag can use extra disk space, and might trigger bugs.
> 
> Signed-off-by: Adam Borowski <kilobyte@angband.pl>
> ---
>  fs/btrfs/ioctl.c | 3 ++-
>  1 file changed, 2 insertions(+), 1 deletion(-)
> 
> diff --git a/fs/btrfs/ioctl.c b/fs/btrfs/ioctl.c
> index 43ecbe620dea..01c150b6ab62 100644
> --- a/fs/btrfs/ioctl.c
> +++ b/fs/btrfs/ioctl.c
> @@ -2941,7 +2941,8 @@ static int btrfs_ioctl_defrag(struct file *file, void __user *argp)
>  		ret = btrfs_defrag_root(root);
>  		break;
>  	case S_IFREG:
> -		if (!(file->f_mode & FMODE_WRITE)) {
> +		if (!capable(CAP_SYS_ADMIN) &&
> +		    inode_permission(inode, MAY_WRITE)) {

The dedupe ioctl does the admin check or the FMODE_WRITE, which means
admin can dedupe anything but user has to have the file write open.
Doing the same for defrag should be ok for same reasons it is for
dedupe.

I'm not sure about using plain inode_permissions here, though it looks
correct and I'm not able to see inode vs file descriptor issues that
could cause trouble here. There are uid/gid, rws, acl, immutable,
capabilities, namespace aware checks, security hooks.

So, I'll add the patch to 4.19 queue. It's small and isolated change so
a revert would be easy in case we find something bad. The 2nd patch
should be IMHO part of this change as it's logical to return the error
code in the patch that modifies the user visible behaviour.

^ permalink raw reply

* Re: [PATCH 10/14] format-patch: add --range-diff option to embed diff in cover letter
From: Duy Nguyen @ 2018-07-23 16:28 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Git Mailing List, Johannes Schindelin,
	Ævar Arnfjörð Bjarmason, Stefan Beller
In-Reply-To: <20180722095717.17912-11-sunshine@sunshineco.com>

On Sun, Jul 22, 2018 at 11:58 AM Eric Sunshine <sunshine@sunshineco.com> wrote:
> diff --git a/Documentation/git-format-patch.txt b/Documentation/git-format-patch.txt
> index f8a061794d..e7f404be3d 100644
> --- a/Documentation/git-format-patch.txt
> +++ b/Documentation/git-format-patch.txt
> @@ -24,6 +24,7 @@ SYNOPSIS
>                    [--to=<email>] [--cc=<email>]
>                    [--[no-]cover-letter] [--quiet] [--notes[=<ref>]]
>                    [--interdiff=<previous>]
> +                  [--range-diff=<previous>]

I wonder if people will use both --interdiff=<rev> and
--range-diff=<rev> often enough to justify a shortcut
"--all-kinds-of-diff=<rev>" so that we don't have to type <previous>
twice. But I guess we don't have to worry about this right now.
-- 
Duy

^ permalink raw reply

* Re: [PATCH 11/20] wil6210: send L2UF on behalf of newly associated station
From: merez @ 2018-07-23 15:27 UTC (permalink / raw)
  To: Johannes Berg
  Cc: Kalle Valo, Ahmad Masri, linux-wireless, wil6210,
	linux-wireless-owner
In-Reply-To: <1532289708.3680.3.camel@sipsolutions.net>

On 2018-07-22 23:01, Johannes Berg wrote:
> On Sun, 2018-07-22 at 10:47 +0300, Maya Erez wrote:
>> From: Ahmad Masri <amasri@codeaurora.org>
>> 
>> Send L2UF (Level 2 Update Frame) to update forwarding tables in layer 
>> 2
>> in AP mode. Wil6210 driver creates and sends this frame once a new
>> station connects to the AP.
> 
> Might be worth unifying with ieee80211_send_layer2_update() into a new
> cfg80211 function?
> 
> johannes
We will do that. I'll revert this patch.
-- 
Maya Erez
Qualcomm Israel, Inc. on behalf of Qualcomm Innovation Center, Inc.
The Qualcomm Innovation Center, Inc. is a member of Code Aurora Forum, a 
Linux Foundation Collaborative Project

^ permalink raw reply

* Re: [PATCH libdrm] tests/modetest: Add modetest_atomic tool
From: Eric Engestrom @ 2018-07-23 16:30 UTC (permalink / raw)
  To: Benjamin Gaignard; +Cc: dri-devel
In-Reply-To: <20180720113329.9789-1-benjamin.gaignard@linaro.org>

On Friday, 2018-07-20 13:33:29 +0200, Benjamin Gaignard wrote:
> This is a modetest like tool but using atomic API.
> 
> With modetest_atomic it is mandatory to specify a mode ("-s")
> and a plane ("-P") to display a pattern on screen.
> 
> "-v" does a loop swapping between two framebuffers for each
> active planes.
> 
> modetest_atomic doesn't offer cursor support
> 
> Signed-off-by: Benjamin Gaignard <benjamin.gaignard@linaro.org>
> ---
> 
> The code is based on modetest and keep most of it infrastructure
> like arguments parsing, finding properties id from their name,
> resources dumping or the general way of working. 
> It duplicates modetest code but adding compilation flags or 
> conditional tests everywhere in modetest would have made it 
> more complex and unreadable.

I don't have an opinion on whether duplicating the test is the right
thing, but if you do, please also duplicate the lines in
tests/modetest/meson.build :)

> 
> Creating modetest_atomic could allow to test atomic API without
> need to use "big" frameworks like weston, drm_hwcomposer or igt
> with all their dependencies.
> kmscube could also be used to test atomic API but it need EGL.
> 
> It have been tested (only) on stm driver with one or two planes
> actived.
> 
>  tests/modetest/Makefile.am       |   13 +-
>  tests/modetest/Makefile.sources  |    7 +
>  tests/modetest/modetest_atomic.c | 1546 ++++++++++++++++++++++++++++++++++++++
>  3 files changed, 1564 insertions(+), 2 deletions(-)
>  create mode 100644 tests/modetest/modetest_atomic.c
> 
> diff --git a/tests/modetest/Makefile.am b/tests/modetest/Makefile.am
> index 4b296c83..8f697bb3 100644
> --- a/tests/modetest/Makefile.am
> +++ b/tests/modetest/Makefile.am
> @@ -10,10 +10,12 @@ AM_CFLAGS += \
>  
>  if HAVE_INSTALL_TESTS
>  bin_PROGRAMS = \
> -	modetest
> +	modetest \
> +	modetest_atomic
>  else
>  noinst_PROGRAMS = \
> -	modetest
> +	modetest \
> +	modetest_atomic
>  endif
>  
>  modetest_SOURCES = $(MODETEST_FILES)
> @@ -22,3 +24,10 @@ modetest_LDADD = \
>  	$(top_builddir)/libdrm.la \
>  	$(top_builddir)/tests/util/libutil.la \
>  	$(CAIRO_LIBS)
> +
> +modetest_atomic_SOURCES = $(MODETEST_ATOMIC_FILES)
> +
> +modetest_atomic_LDADD = \
> +	$(top_builddir)/libdrm.la \
> +	$(top_builddir)/tests/util/libutil.la \
> +	$(CAIRO_LIBS)
> diff --git a/tests/modetest/Makefile.sources b/tests/modetest/Makefile.sources
> index 399af0df..0a1df4c0 100644
> --- a/tests/modetest/Makefile.sources
> +++ b/tests/modetest/Makefile.sources
> @@ -4,3 +4,10 @@ MODETEST_FILES := \
>  	cursor.c \
>  	cursor.h \
>  	modetest.c
> +
> +MODETEST_ATOMIC_FILES := \
> +	buffers.c \
> +	buffers.h \
> +	cursor.c \
> +	cursor.h \
> +	modetest_atomic.c
> diff --git a/tests/modetest/modetest_atomic.c b/tests/modetest/modetest_atomic.c
> new file mode 100644
> index 00000000..8c877860
> --- /dev/null
> +++ b/tests/modetest/modetest_atomic.c
> @@ -0,0 +1,1546 @@
> +/*
> + * DRM based mode setting test program
> + * Copyright 2008 Tungsten Graphics
> + *   Jakob Bornecrantz <jakob@tungstengraphics.com>
> + * Copyright 2008 Intel Corporation
> + *   Jesse Barnes <jesse.barnes@intel.com>
> + *
> + * Permission is hereby granted, free of charge, to any person obtaining a
> + * copy of this software and associated documentation files (the "Software"),
> + * to deal in the Software without restriction, including without limitation
> + * the rights to use, copy, modify, merge, publish, distribute, sublicense,
> + * and/or sell copies of the Software, and to permit persons to whom the
> + * Software is furnished to do so, subject to the following conditions:
> + *
> + * The above copyright notice and this permission notice shall be included in
> + * all copies or substantial portions of the Software.
> + *
> + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
> + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
> + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
> + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
> + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
> + * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
> + * IN THE SOFTWARE.
> + */
> +
> +/*
> + * This fairly simple test program dumps output in a similar format to the
> + * "xrandr" tool everyone knows & loves.  It's necessarily slightly different
> + * since the kernel separates outputs into encoder and connector structures,
> + * each with their own unique ID.  The program also allows test testing of the
> + * memory management and mode setting APIs by allowing the user to specify a
> + * connector and mode to use for mode setting.  If all works as expected, a
> + * blue background should be painted on the monitor attached to the specified
> + * connector after the selected mode is set.
> + *
> + * TODO: use cairo to write the mode info on the selected output once
> + *       the mode has been programmed, along with possible test patterns.
> + */
> +
> +#ifdef HAVE_CONFIG_H
> +#include "config.h"
> +#endif
> +
> +#include <assert.h>
> +#include <ctype.h>
> +#include <stdbool.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <stdint.h>
> +#include <inttypes.h>
> +#include <unistd.h>
> +#include <string.h>
> +#include <strings.h>
> +#include <errno.h>
> +#include <poll.h>
> +#include <sys/time.h>
> +#ifdef HAVE_SYS_SELECT_H
> +#include <sys/select.h>
> +#endif
> +
> +#include "xf86drm.h"
> +#include "xf86drmMode.h"
> +#include "drm_fourcc.h"
> +
> +#include "util/common.h"
> +#include "util/format.h"
> +#include "util/kms.h"
> +#include "util/pattern.h"
> +
> +#include "buffers.h"
> +
> +struct crtc {
> +	drmModeCrtc *crtc;
> +	drmModeObjectProperties *props;
> +	drmModePropertyRes **props_info;
> +	drmModeModeInfo *mode;
> +};
> +
> +struct encoder {
> +	drmModeEncoder *encoder;
> +};
> +
> +struct connector {
> +	drmModeConnector *connector;
> +	drmModeObjectProperties *props;
> +	drmModePropertyRes **props_info;
> +	char *name;
> +};
> +
> +struct fb {
> +	drmModeFB *fb;
> +};
> +
> +struct plane {
> +	drmModePlane *plane;
> +	drmModeObjectProperties *props;
> +	drmModePropertyRes **props_info;
> +};
> +
> +struct resources {
> +	drmModeRes *res;
> +	drmModePlaneRes *plane_res;
> +
> +	struct crtc *crtcs;
> +	struct encoder *encoders;
> +	struct connector *connectors;
> +	struct fb *fbs;
> +	struct plane *planes;
> +};
> +
> +struct device {
> +	int fd;
> +
> +	struct resources *resources;
> +	drmModeAtomicReq *req;
> +};
> +
> +static inline int64_t U642I64(uint64_t val)
> +{
> +	return (int64_t)*((int64_t *)&val);
> +}
> +
> +#define bit_name_fn(res)					\
> +const char * res##_str(int type) {				\
> +	unsigned int i;						\
> +	const char *sep = "";					\
> +	for (i = 0; i < ARRAY_SIZE(res##_names); i++) {		\
> +		if (type & (1 << i)) {				\
> +			printf("%s%s", sep, res##_names[i]);	\
> +			sep = ", ";				\
> +		}						\
> +	}							\
> +	return NULL;						\
> +}
> +
> +static const char *mode_type_names[] = {
> +	"builtin",
> +	"clock_c",
> +	"crtc_c",
> +	"preferred",
> +	"default",
> +	"userdef",
> +	"driver",
> +};
> +
> +static bit_name_fn(mode_type)
> +
> +static const char *mode_flag_names[] = {
> +	"phsync",
> +	"nhsync",
> +	"pvsync",
> +	"nvsync",
> +	"interlace",
> +	"dblscan",
> +	"csync",
> +	"pcsync",
> +	"ncsync",
> +	"hskew",
> +	"bcast",
> +	"pixmux",
> +	"dblclk",
> +	"clkdiv2"
> +};
> +
> +static bit_name_fn(mode_flag)
> +
> +static void dump_fourcc(uint32_t fourcc)
> +{
> +	printf(" %c%c%c%c",
> +		fourcc,
> +		fourcc >> 8,
> +		fourcc >> 16,
> +		fourcc >> 24);
> +}
> +
> +static void dump_encoders(struct device *dev)
> +{
> +	drmModeEncoder *encoder;
> +	int i;
> +
> +	printf("Encoders:\n");
> +	printf("id\tcrtc\ttype\tpossible crtcs\tpossible clones\t\n");
> +	for (i = 0; i < dev->resources->res->count_encoders; i++) {
> +		encoder = dev->resources->encoders[i].encoder;
> +		if (!encoder)
> +			continue;
> +
> +		printf("%d\t%d\t%s\t0x%08x\t0x%08x\n",
> +		       encoder->encoder_id,
> +		       encoder->crtc_id,
> +		       util_lookup_encoder_type_name(encoder->encoder_type),
> +		       encoder->possible_crtcs,
> +		       encoder->possible_clones);
> +	}
> +	printf("\n");
> +}
> +
> +static void dump_mode(drmModeModeInfo *mode)
> +{
> +	printf("  %s %d %d %d %d %d %d %d %d %d %d",
> +	       mode->name,
> +	       mode->vrefresh,
> +	       mode->hdisplay,
> +	       mode->hsync_start,
> +	       mode->hsync_end,
> +	       mode->htotal,
> +	       mode->vdisplay,
> +	       mode->vsync_start,
> +	       mode->vsync_end,
> +	       mode->vtotal,
> +	       mode->clock);
> +
> +	printf(" flags: ");
> +	mode_flag_str(mode->flags);
> +	printf("; type: ");
> +	mode_type_str(mode->type);
> +	printf("\n");
> +}
> +
> +static void dump_blob(struct device *dev, uint32_t blob_id)
> +{
> +	uint32_t i;
> +	unsigned char *blob_data;
> +	drmModePropertyBlobPtr blob;
> +
> +	blob = drmModeGetPropertyBlob(dev->fd, blob_id);
> +	if (!blob) {
> +		printf("\n");
> +		return;
> +	}
> +
> +	blob_data = blob->data;
> +
> +	for (i = 0; i < blob->length; i++) {
> +		if (i % 16 == 0)
> +			printf("\n\t\t\t");
> +		printf("%.2hhx", blob_data[i]);
> +	}
> +	printf("\n");
> +
> +	drmModeFreePropertyBlob(blob);
> +}
> +
> +static void dump_prop(struct device *dev, drmModePropertyPtr prop,
> +		      uint32_t prop_id, uint64_t value)
> +{
> +	int i;
> +	printf("\t%d", prop_id);
> +	if (!prop) {
> +		printf("\n");
> +		return;
> +	}
> +
> +	printf(" %s:\n", prop->name);
> +
> +	printf("\t\tflags:");
> +	if (prop->flags & DRM_MODE_PROP_PENDING)
> +		printf(" pending");
> +	if (prop->flags & DRM_MODE_PROP_IMMUTABLE)
> +		printf(" immutable");
> +	if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE))
> +		printf(" signed range");
> +	if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE))
> +		printf(" range");
> +	if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM))
> +		printf(" enum");
> +	if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK))
> +		printf(" bitmask");
> +	if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB))
> +		printf(" blob");
> +	if (drm_property_type_is(prop, DRM_MODE_PROP_OBJECT))
> +		printf(" object");
> +	printf("\n");
> +
> +	if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE)) {
> +		printf("\t\tvalues:");
> +		for (i = 0; i < prop->count_values; i++)
> +			printf(" %"PRId64, U642I64(prop->values[i]));
> +		printf("\n");
> +	}
> +
> +	if (drm_property_type_is(prop, DRM_MODE_PROP_RANGE)) {
> +		printf("\t\tvalues:");
> +		for (i = 0; i < prop->count_values; i++)
> +			printf(" %"PRIu64, prop->values[i]);
> +		printf("\n");
> +	}
> +
> +	if (drm_property_type_is(prop, DRM_MODE_PROP_ENUM)) {
> +		printf("\t\tenums:");
> +		for (i = 0; i < prop->count_enums; i++)
> +			printf(" %s=%llu", prop->enums[i].name,
> +			       prop->enums[i].value);
> +		printf("\n");
> +	} else if (drm_property_type_is(prop, DRM_MODE_PROP_BITMASK)) {
> +		printf("\t\tvalues:");
> +		for (i = 0; i < prop->count_enums; i++)
> +			printf(" %s=0x%llx", prop->enums[i].name,
> +			       (1LL << prop->enums[i].value));
> +		printf("\n");
> +	} else {
> +		assert(prop->count_enums == 0);
> +	}
> +
> +	if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB)) {
> +		printf("\t\tblobs:\n");
> +		for (i = 0; i < prop->count_blobs; i++)
> +			dump_blob(dev, prop->blob_ids[i]);
> +		printf("\n");
> +	} else {
> +		assert(prop->count_blobs == 0);
> +	}
> +
> +	printf("\t\tvalue:");
> +	if (drm_property_type_is(prop, DRM_MODE_PROP_BLOB))
> +		dump_blob(dev, value);
> +	else if (drm_property_type_is(prop, DRM_MODE_PROP_SIGNED_RANGE))
> +		printf(" %"PRId64"\n", value);
> +	else
> +		printf(" %"PRIu64"\n", value);
> +}
> +
> +static void dump_connectors(struct device *dev)
> +{
> +	int i, j;
> +
> +	printf("Connectors:\n");
> +	printf("id\tencoder\tstatus\t\tname\t\tsize (mm)\tmodes\tencoders\n");
> +	for (i = 0; i < dev->resources->res->count_connectors; i++) {
> +		struct connector *_connector = &dev->resources->connectors[i];
> +		drmModeConnector *connector = _connector->connector;
> +		if (!connector)
> +			continue;
> +
> +		printf("%d\t%d\t%s\t%-15s\t%dx%d\t\t%d\t",
> +		       connector->connector_id,
> +		       connector->encoder_id,
> +		       util_lookup_connector_status_name(connector->connection),
> +		       _connector->name,
> +		       connector->mmWidth, connector->mmHeight,
> +		       connector->count_modes);
> +
> +		for (j = 0; j < connector->count_encoders; j++)
> +			printf("%s%d", j > 0 ? ", " : "", connector->encoders[j]);
> +		printf("\n");
> +
> +		if (connector->count_modes) {
> +			printf("  modes:\n");
> +			printf("\tname refresh (Hz) hdisp hss hse htot vdisp "
> +			       "vss vse vtot)\n");
> +			for (j = 0; j < connector->count_modes; j++)
> +				dump_mode(&connector->modes[j]);
> +		}
> +
> +		if (_connector->props) {
> +			printf("  props:\n");
> +			for (j = 0; j < (int)_connector->props->count_props; j++)
> +				dump_prop(dev, _connector->props_info[j],
> +					  _connector->props->props[j],
> +					  _connector->props->prop_values[j]);
> +		}
> +	}
> +	printf("\n");
> +}
> +
> +static void dump_crtcs(struct device *dev)
> +{
> +	int i;
> +	uint32_t j;
> +
> +	printf("CRTCs:\n");
> +	printf("id\tfb\tpos\tsize\n");
> +	for (i = 0; i < dev->resources->res->count_crtcs; i++) {
> +		struct crtc *_crtc = &dev->resources->crtcs[i];
> +		drmModeCrtc *crtc = _crtc->crtc;
> +		if (!crtc)
> +			continue;
> +
> +		printf("%d\t%d\t(%d,%d)\t(%dx%d)\n",
> +		       crtc->crtc_id,
> +		       crtc->buffer_id,
> +		       crtc->x, crtc->y,
> +		       crtc->width, crtc->height);
> +		dump_mode(&crtc->mode);
> +
> +		if (_crtc->props) {
> +			printf("  props:\n");
> +			for (j = 0; j < _crtc->props->count_props; j++)
> +				dump_prop(dev, _crtc->props_info[j],
> +					  _crtc->props->props[j],
> +					  _crtc->props->prop_values[j]);
> +		} else {
> +			printf("  no properties found\n");
> +		}
> +	}
> +	printf("\n");
> +}
> +
> +static void dump_framebuffers(struct device *dev)
> +{
> +	drmModeFB *fb;
> +	int i;
> +
> +	printf("Frame buffers:\n");
> +	printf("id\tsize\tpitch\n");
> +	for (i = 0; i < dev->resources->res->count_fbs; i++) {
> +		fb = dev->resources->fbs[i].fb;
> +		if (!fb)
> +			continue;
> +
> +		printf("%u\t(%ux%u)\t%u\n",
> +		       fb->fb_id,
> +		       fb->width, fb->height,
> +		       fb->pitch);
> +	}
> +	printf("\n");
> +}
> +
> +static void dump_planes(struct device *dev)
> +{
> +	unsigned int i, j;
> +
> +	printf("Planes:\n");
> +	printf("id\tcrtc\tfb\tCRTC x,y\tx,y\tgamma size\tpossible crtcs\n");
> +
> +	if (!dev->resources->plane_res)
> +		return;
> +
> +	for (i = 0; i < dev->resources->plane_res->count_planes; i++) {
> +		struct plane *plane = &dev->resources->planes[i];
> +		drmModePlane *ovr = plane->plane;
> +		if (!ovr)
> +			continue;
> +
> +		printf("%d\t%d\t%d\t%d,%d\t\t%d,%d\t%-8d\t0x%08x\n",
> +		       ovr->plane_id, ovr->crtc_id, ovr->fb_id,
> +		       ovr->crtc_x, ovr->crtc_y, ovr->x, ovr->y,
> +		       ovr->gamma_size, ovr->possible_crtcs);
> +
> +		if (!ovr->count_formats)
> +			continue;
> +
> +		printf("  formats:");
> +		for (j = 0; j < ovr->count_formats; j++)
> +			dump_fourcc(ovr->formats[j]);
> +		printf("\n");
> +
> +		if (plane->props) {
> +			printf("  props:\n");
> +			for (j = 0; j < plane->props->count_props; j++)
> +				dump_prop(dev, plane->props_info[j],
> +					  plane->props->props[j],
> +					  plane->props->prop_values[j]);
> +		} else {
> +			printf("  no properties found\n");
> +		}
> +	}
> +	printf("\n");
> +
> +	return;
> +}
> +
> +static void free_resources(struct resources *res)
> +{
> +	int i;
> +
> +	if (!res)
> +		return;
> +
> +#define free_resource(_res, __res, type, Type)					\
> +	do {									\
> +		if (!(_res)->type##s)						\
> +			break;							\
> +		for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {	\
> +			if (!(_res)->type##s[i].type)				\
> +				break;						\
> +			drmModeFree##Type((_res)->type##s[i].type);		\
> +		}								\
> +		free((_res)->type##s);						\
> +	} while (0)
> +
> +#define free_properties(_res, __res, type)					\
> +	do {									\
> +		for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {	\
> +			drmModeFreeObjectProperties(res->type##s[i].props);	\
> +			free(res->type##s[i].props_info);			\
> +		}								\
> +	} while (0)
> +
> +	if (res->res) {
> +		free_properties(res, res, crtc);
> +
> +		free_resource(res, res, crtc, Crtc);
> +		free_resource(res, res, encoder, Encoder);
> +
> +		for (i = 0; i < res->res->count_connectors; i++)
> +			free(res->connectors[i].name);
> +
> +		free_resource(res, res, connector, Connector);
> +		free_resource(res, res, fb, FB);
> +
> +		drmModeFreeResources(res->res);
> +	}
> +
> +	if (res->plane_res) {
> +		free_properties(res, plane_res, plane);
> +
> +		free_resource(res, plane_res, plane, Plane);
> +
> +		drmModeFreePlaneResources(res->plane_res);
> +	}
> +
> +	free(res);
> +}
> +
> +static struct resources *get_resources(struct device *dev)
> +{
> +	struct resources *res;
> +	int i;
> +
> +	res = calloc(1, sizeof(*res));
> +	if (res == 0)
> +		return NULL;
> +
> +	drmSetClientCap(dev->fd, DRM_CLIENT_CAP_UNIVERSAL_PLANES, 1);
> +
> +	res->res = drmModeGetResources(dev->fd);
> +	if (!res->res) {
> +		fprintf(stderr, "drmModeGetResources failed: %s\n",
> +			strerror(errno));
> +		goto error;
> +	}
> +
> +	res->crtcs = calloc(res->res->count_crtcs, sizeof(*res->crtcs));
> +	res->encoders = calloc(res->res->count_encoders, sizeof(*res->encoders));
> +	res->connectors = calloc(res->res->count_connectors, sizeof(*res->connectors));
> +	res->fbs = calloc(res->res->count_fbs, sizeof(*res->fbs));
> +
> +	if (!res->crtcs || !res->encoders || !res->connectors || !res->fbs)
> +		goto error;
> +
> +#define get_resource(_res, __res, type, Type)					\
> +	do {									\
> +		for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {	\
> +			(_res)->type##s[i].type =				\
> +				drmModeGet##Type(dev->fd, (_res)->__res->type##s[i]); \
> +			if (!(_res)->type##s[i].type)				\
> +				fprintf(stderr, "could not get %s %i: %s\n",	\
> +					#type, (_res)->__res->type##s[i],	\
> +					strerror(errno));			\
> +		}								\
> +	} while (0)
> +
> +	get_resource(res, res, crtc, Crtc);
> +	get_resource(res, res, encoder, Encoder);
> +	get_resource(res, res, connector, Connector);
> +	get_resource(res, res, fb, FB);
> +
> +	/* Set the name of all connectors based on the type name and the per-type ID. */
> +	for (i = 0; i < res->res->count_connectors; i++) {
> +		struct connector *connector = &res->connectors[i];
> +		drmModeConnector *conn = connector->connector;
> +		int num;
> +
> +		num = asprintf(&connector->name, "%s-%u",
> +			 util_lookup_connector_type_name(conn->connector_type),
> +			 conn->connector_type_id);
> +		if (num < 0)
> +			goto error;
> +	}
> +
> +#define get_properties(_res, __res, type, Type)					\
> +	do {									\
> +		for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {	\
> +			struct type *obj = &res->type##s[i];			\
> +			unsigned int j;						\
> +			obj->props =						\
> +				drmModeObjectGetProperties(dev->fd, obj->type->type##_id, \
> +							   DRM_MODE_OBJECT_##Type); \
> +			if (!obj->props) {					\
> +				fprintf(stderr,					\
> +					"could not get %s %i properties: %s\n", \
> +					#type, obj->type->type##_id,		\
> +					strerror(errno));			\
> +				continue;					\
> +			}							\
> +			obj->props_info = calloc(obj->props->count_props,	\
> +						 sizeof(*obj->props_info));	\
> +			if (!obj->props_info)					\
> +				continue;					\
> +			for (j = 0; j < obj->props->count_props; ++j)		\
> +				obj->props_info[j] =				\
> +					drmModeGetProperty(dev->fd, obj->props->props[j]); \
> +		}								\
> +	} while (0)
> +
> +	get_properties(res, res, crtc, CRTC);
> +	get_properties(res, res, connector, CONNECTOR);
> +
> +	for (i = 0; i < res->res->count_crtcs; ++i)
> +		res->crtcs[i].mode = &res->crtcs[i].crtc->mode;
> +
> +	res->plane_res = drmModeGetPlaneResources(dev->fd);
> +	if (!res->plane_res) {
> +		fprintf(stderr, "drmModeGetPlaneResources failed: %s\n",
> +			strerror(errno));
> +		return res;
> +	}
> +
> +	res->planes = calloc(res->plane_res->count_planes, sizeof(*res->planes));
> +	if (!res->planes)
> +		goto error;
> +
> +	get_resource(res, plane_res, plane, Plane);
> +	get_properties(res, plane_res, plane, PLANE);
> +
> +	return res;
> +
> +error:
> +	free_resources(res);
> +	return NULL;
> +}
> +
> +static int get_crtc_index(struct device *dev, uint32_t id)
> +{
> +	int i;
> +
> +	for (i = 0; i < dev->resources->res->count_crtcs; ++i) {
> +		drmModeCrtc *crtc = dev->resources->crtcs[i].crtc;
> +		if (crtc && crtc->crtc_id == id)
> +			return i;
> +	}
> +
> +	return -1;
> +}
> +
> +static drmModeConnector *get_connector_by_name(struct device *dev, const char *name)
> +{
> +	struct connector *connector;
> +	int i;
> +
> +	for (i = 0; i < dev->resources->res->count_connectors; i++) {
> +		connector = &dev->resources->connectors[i];
> +
> +		if (strcmp(connector->name, name) == 0)
> +			return connector->connector;
> +	}
> +
> +	return NULL;
> +}
> +
> +static drmModeConnector *get_connector_by_id(struct device *dev, uint32_t id)
> +{
> +	drmModeConnector *connector;
> +	int i;
> +
> +	for (i = 0; i < dev->resources->res->count_connectors; i++) {
> +		connector = dev->resources->connectors[i].connector;
> +		if (connector && connector->connector_id == id)
> +			return connector;
> +	}
> +
> +	return NULL;
> +}
> +
> +static drmModeEncoder *get_encoder_by_id(struct device *dev, uint32_t id)
> +{
> +	drmModeEncoder *encoder;
> +	int i;
> +
> +	for (i = 0; i < dev->resources->res->count_encoders; i++) {
> +		encoder = dev->resources->encoders[i].encoder;
> +		if (encoder && encoder->encoder_id == id)
> +			return encoder;
> +	}
> +
> +	return NULL;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Pipes and planes
> + */
> +
> +/*
> + * Mode setting with the kernel interfaces is a bit of a chore.
> + * First you have to find the connector in question and make sure the
> + * requested mode is available.
> + * Then you need to find the encoder attached to that connector so you
> + * can bind it with a free crtc.
> + */
> +struct pipe_arg {
> +	const char **cons;
> +	uint32_t *con_ids;
> +	unsigned int num_cons;
> +	uint32_t crtc_id;
> +	char mode_str[64];
> +	char format_str[5];
> +	unsigned int vrefresh;
> +	unsigned int fourcc;
> +	drmModeModeInfo *mode;
> +	struct crtc *crtc;
> +	unsigned int fb_id[2], current_fb_id;
> +	struct timeval start;
> +
> +	int swap_count;
> +};
> +
> +struct plane_arg {
> +	uint32_t plane_id;  /* the id of plane to use */
> +	uint32_t crtc_id;  /* the id of CRTC to bind to */
> +	bool has_position;
> +	int32_t x, y;
> +	uint32_t w, h;
> +	double scale;
> +	unsigned int fb_id;
> +	unsigned int old_fb_id;
> +	struct bo *bo;
> +	struct bo *old_bo;
> +	char format_str[5]; /* need to leave room for terminating \0 */
> +	unsigned int fourcc;
> +};
> +
> +static drmModeModeInfo *
> +connector_find_mode(struct device *dev, uint32_t con_id, const char *mode_str,
> +        const unsigned int vrefresh)
> +{
> +	drmModeConnector *connector;
> +	drmModeModeInfo *mode;
> +	int i;
> +
> +	connector = get_connector_by_id(dev, con_id);
> +	if (!connector || !connector->count_modes)
> +		return NULL;
> +
> +	for (i = 0; i < connector->count_modes; i++) {
> +		mode = &connector->modes[i];
> +		if (!strcmp(mode->name, mode_str)) {
> +			/* If the vertical refresh frequency is not specified then return the
> +			 * first mode that match with the name. Else, return the mode that match
> +			 * the name and the specified vertical refresh frequency.
> +			 */
> +			if (vrefresh == 0)
> +				return mode;
> +			else if (mode->vrefresh == vrefresh)
> +				return mode;
> +		}
> +	}
> +
> +	return NULL;
> +}
> +
> +static struct crtc *pipe_find_crtc(struct device *dev, struct pipe_arg *pipe)
> +{
> +	uint32_t possible_crtcs = ~0;
> +	uint32_t active_crtcs = 0;
> +	unsigned int crtc_idx;
> +	unsigned int i;
> +	int j;
> +
> +	for (i = 0; i < pipe->num_cons; ++i) {
> +		uint32_t crtcs_for_connector = 0;
> +		drmModeConnector *connector;
> +		drmModeEncoder *encoder;
> +		int idx;
> +
> +		connector = get_connector_by_id(dev, pipe->con_ids[i]);
> +		if (!connector)
> +			return NULL;
> +
> +		for (j = 0; j < connector->count_encoders; ++j) {
> +			encoder = get_encoder_by_id(dev, connector->encoders[j]);
> +			if (!encoder)
> +				continue;
> +
> +			crtcs_for_connector |= encoder->possible_crtcs;
> +
> +			idx = get_crtc_index(dev, encoder->crtc_id);
> +			if (idx >= 0)
> +				active_crtcs |= 1 << idx;
> +		}
> +
> +		possible_crtcs &= crtcs_for_connector;
> +	}
> +
> +	if (!possible_crtcs)
> +		return NULL;
> +
> +	/* Return the first possible and active CRTC if one exists, or the first
> +	 * possible CRTC otherwise.
> +	 */
> +	if (possible_crtcs & active_crtcs)
> +		crtc_idx = ffs(possible_crtcs & active_crtcs);
> +	else
> +		crtc_idx = ffs(possible_crtcs);
> +
> +	return &dev->resources->crtcs[crtc_idx - 1];
> +}
> +
> +static int pipe_find_crtc_and_mode(struct device *dev, struct pipe_arg *pipe)
> +{
> +	drmModeModeInfo *mode = NULL;
> +	int i;
> +
> +	pipe->mode = NULL;
> +
> +	for (i = 0; i < (int)pipe->num_cons; i++) {
> +		mode = connector_find_mode(dev, pipe->con_ids[i],
> +					   pipe->mode_str, pipe->vrefresh);
> +		if (mode == NULL) {
> +			fprintf(stderr,
> +				"failed to find mode \"%s\" for connector %s\n",
> +				pipe->mode_str, pipe->cons[i]);
> +			return -EINVAL;
> +		}
> +	}
> +
> +	/* If the CRTC ID was specified, get the corresponding CRTC. Otherwise
> +	 * locate a CRTC that can be attached to all the connectors.
> +	 */
> +	if (pipe->crtc_id != (uint32_t)-1) {
> +		for (i = 0; i < dev->resources->res->count_crtcs; i++) {
> +			struct crtc *crtc = &dev->resources->crtcs[i];
> +
> +			if (pipe->crtc_id == crtc->crtc->crtc_id) {
> +				pipe->crtc = crtc;
> +				break;
> +			}
> +		}
> +	} else {
> +		pipe->crtc = pipe_find_crtc(dev, pipe);
> +	}
> +
> +	if (!pipe->crtc) {
> +		fprintf(stderr, "failed to find CRTC for pipe\n");
> +		return -EINVAL;
> +	}
> +
> +	pipe->mode = mode;
> +	pipe->crtc->mode = mode;
> +
> +	return 0;
> +}
> +
> +/* -----------------------------------------------------------------------------
> + * Properties
> + */
> +
> +struct property_arg {
> +	uint32_t obj_id;
> +	uint32_t obj_type;
> +	char name[DRM_PROP_NAME_LEN+1];
> +	uint32_t prop_id;
> +	uint64_t value;
> +};
> +
> +static void set_property(struct device *dev, struct property_arg *p)
> +{
> +	drmModeObjectProperties *props = NULL;
> +	drmModePropertyRes **props_info = NULL;
> +	const char *obj_type;
> +	int ret;
> +	int i;
> +
> +	p->obj_type = 0;
> +	p->prop_id = 0;
> +
> +#define find_object(_res, __res, type, Type)					\
> +	do {									\
> +		for (i = 0; i < (int)(_res)->__res->count_##type##s; ++i) {	\
> +			struct type *obj = &(_res)->type##s[i];			\
> +			if (obj->type->type##_id != p->obj_id)			\
> +				continue;					\
> +			p->obj_type = DRM_MODE_OBJECT_##Type;			\
> +			obj_type = #Type;					\
> +			props = obj->props;					\
> +			props_info = obj->props_info;				\
> +		}								\
> +	} while(0)								\
> +
> +	find_object(dev->resources, res, crtc, CRTC);
> +	if (p->obj_type == 0)
> +		find_object(dev->resources, res, connector, CONNECTOR);
> +	if (p->obj_type == 0)
> +		find_object(dev->resources, plane_res, plane, PLANE);
> +	if (p->obj_type == 0) {
> +		fprintf(stderr, "Object %i not found, can't set property\n",
> +			p->obj_id);
> +			return;
> +	}
> +
> +	if (!props) {
> +		fprintf(stderr, "%s %i has no properties\n",
> +			obj_type, p->obj_id);
> +		return;
> +	}
> +
> +	for (i = 0; i < (int)props->count_props; ++i) {
> +		if (!props_info[i])
> +			continue;
> +		if (strcmp(props_info[i]->name, p->name) == 0)
> +			break;
> +	}
> +
> +	if (i == (int)props->count_props) {
> +		fprintf(stderr, "%s %i has no %s property\n",
> +			obj_type, p->obj_id, p->name);
> +		return;
> +	}
> +
> +	p->prop_id = props->props[i];
> +
> +	ret = drmModeAtomicAddProperty(dev->req, p->obj_id, p->prop_id, p->value);
> +	if (ret < 0)
> +		fprintf(stderr, "failed to set %s %i property %s to %" PRIu64 ": %s\n",
> +			obj_type, p->obj_id, p->name, p->value, strerror(errno));
> +}
> +
> +/* -------------------------------------------------------------------------- */
> +
> +/*static bool format_support(const drmModePlanePtr ovr, uint32_t fmt)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < ovr->count_formats; ++i) {
> +		if (ovr->formats[i] == fmt)
> +			return true;
> +	}
> +
> +	return false;
> +}*/
> +
> +static void add_property(struct device *dev, uint32_t obj_id,
> +			       const char *name, uint64_t value)
> +{
> +	struct property_arg p;
> +
> +	p.obj_id = obj_id;
> +	strcpy(p.name, name);
> +	p.value = value;
> +
> +	set_property(dev, &p);
> +}
> +
> +static int set_plane(struct device *dev, struct plane_arg *p,
> +		     int pattern, bool update)
> +{
> +	uint32_t handles[4] = {0}, pitches[4] = {0}, offsets[4] = {0};
> +	struct bo *plane_bo;
> +	int crtc_x, crtc_y, crtc_w, crtc_h;
> +	struct crtc *crtc = NULL;
> +	unsigned int i;
> +	unsigned int old_fb_id;
> +
> +	/* Find an unused plane which can be connected to our CRTC. Find the
> +	 * CRTC index first, then iterate over available planes.
> +	 */
> +	for (i = 0; i < (unsigned int)dev->resources->res->count_crtcs; i++) {
> +		if (p->crtc_id == dev->resources->res->crtcs[i]) {
> +			crtc = &dev->resources->crtcs[i];
> +			break;
> +		}
> +	}
> +
> +	if (!crtc) {
> +		fprintf(stderr, "CRTC %u not found\n", p->crtc_id);
> +		return -1;
> +	}
> +
> +	if (!update)
> +		fprintf(stderr, "testing %dx%d@%s on plane %u, crtc %u\n",
> +			p->w, p->h, p->format_str, p->plane_id, p->crtc_id);
> +
> +	plane_bo = p->old_bo;
> +	p->old_bo = p->bo;
> +
> +	if (!plane_bo) {
> +		plane_bo = bo_create(dev->fd, p->fourcc, p->w, p->h,
> +				     handles, pitches, offsets, pattern);
> +
> +		if (plane_bo == NULL)
> +			return -1;
> +
> +		if (drmModeAddFB2(dev->fd, p->w, p->h, p->fourcc,
> +			handles, pitches, offsets, &p->fb_id, 0)) {
> +			fprintf(stderr, "failed to add fb: %s\n", strerror(errno));
> +			return -1;
> +		}
> +	}
> +
> +	p->bo = plane_bo;
> +
> +	old_fb_id = p->fb_id;
> +	p->old_fb_id = old_fb_id;
> +
> +	crtc_w = p->w * p->scale;
> +	crtc_h = p->h * p->scale;
> +	if (!p->has_position) {
> +		/* Default to the middle of the screen */
> +		crtc_x = (crtc->mode->hdisplay - crtc_w) / 2;
> +		crtc_y = (crtc->mode->vdisplay - crtc_h) / 2;
> +	} else {
> +		crtc_x = p->x;
> +		crtc_y = p->y;
> +	}
> +
> +	add_property(dev, p->plane_id, "FB_ID", p->fb_id);
> +	add_property(dev, p->plane_id, "CRTC_ID", p->crtc_id);
> +	add_property(dev, p->plane_id, "SRC_X", 0);
> +	add_property(dev, p->plane_id, "SRC_Y", 0);
> +	add_property(dev, p->plane_id, "SRC_W", p->w << 16);
> +	add_property(dev, p->plane_id, "SRC_H", p->h << 16);
> +	add_property(dev, p->plane_id, "CRTC_X", crtc_x);
> +	add_property(dev, p->plane_id, "CRTC_Y", crtc_y);
> +	add_property(dev, p->plane_id, "CRTC_W", crtc_w);
> +	add_property(dev, p->plane_id, "CRTC_H", crtc_h);
> +
> +	return 0;
> +}
> +
> +static void set_planes(struct device *dev, struct plane_arg *p,
> +		       unsigned int count, bool update)
> +{
> +	unsigned int i, pattern = UTIL_PATTERN_SMPTE;
> +
> +	/* set up planes */
> +	for (i = 0; i < count; i++) {
> +		if (i > 0)
> +			pattern = UTIL_PATTERN_TILES;
> +
> +		if (set_plane(dev, &p[i], pattern, update))
> +			return;
> +	}
> +}
> +
> +static void clear_planes(struct device *dev, struct plane_arg *p, unsigned int count)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < count; i++) {
> +		add_property(dev, p[i].plane_id, "FB_ID", 0);
> +		add_property(dev, p[i].plane_id, "CRTC_ID", 0);
> +		add_property(dev, p[i].plane_id, "SRC_X", 0);
> +		add_property(dev, p[i].plane_id, "SRC_Y", 0);
> +		add_property(dev, p[i].plane_id, "SRC_W", 0);
> +		add_property(dev, p[i].plane_id, "SRC_H", 0);
> +		add_property(dev, p[i].plane_id, "CRTC_X", 0);
> +		add_property(dev, p[i].plane_id, "CRTC_Y", 0);
> +		add_property(dev, p[i].plane_id, "CRTC_W", 0);
> +		add_property(dev, p[i].plane_id, "CRTC_H", 0);
> +	}
> +}
> +
> +static void clear_FB(struct device *dev, struct plane_arg *p, unsigned int count)
> +{
> +	unsigned int i;
> +
> +	for (i = 0; i < count; i++) {
> +		if (p[i].fb_id) {
> +			drmModeRmFB(dev->fd, p[i].fb_id);
> +			p[i].fb_id = 0;
> +		}
> +		if (p[i].old_fb_id) {
> +			drmModeRmFB(dev->fd, p[i].old_fb_id);
> +			p[i].old_fb_id = 0;
> +		}
> +		if (p[i].bo) {
> +			bo_destroy(p[i].bo);
> +			p[i].bo = NULL;
> +		}
> +		if (p[i].old_bo) {
> +			bo_destroy(p[i].old_bo);
> +			p[i].old_bo = NULL;
> +		}
> +
> +	}
> +}
> +
> +static void set_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
> +{
> +	unsigned int i;
> +	unsigned int j;
> +	int ret;
> +
> +	for (i = 0; i < count; i++) {
> +		struct pipe_arg *pipe = &pipes[i];
> +
> +		ret = pipe_find_crtc_and_mode(dev, pipe);
> +		if (ret < 0)
> +			continue;
> +	}
> +
> +	for (i = 0; i < count; i++) {
> +		struct pipe_arg *pipe = &pipes[i];
> +		uint32_t blob_id;
> +
> +		if (pipe->mode == NULL)
> +			continue;
> +
> +		printf("setting mode %s-%dHz@%s on connectors ",
> +		       pipe->mode_str, pipe->mode->vrefresh, pipe->format_str);
> +		for (j = 0; j < pipe->num_cons; ++j) {
> +			printf("%s, ", pipe->cons[j]);
> +			add_property(dev, pipe->con_ids[j], "CRTC_ID", pipe->crtc->crtc->crtc_id);
> +		}
> +		printf("crtc %d\n", pipe->crtc->crtc->crtc_id);
> +
> +		drmModeCreatePropertyBlob(dev->fd, pipe->mode, sizeof(*pipe->mode), &blob_id);
> +		add_property(dev, pipe->crtc->crtc->crtc_id, "MODE_ID", blob_id);
> +		add_property(dev, pipe->crtc->crtc->crtc_id, "ACTIVE", 1);
> +	}
> +}
> +
> +static void clear_mode(struct device *dev, struct pipe_arg *pipes, unsigned int count)
> +{
> +	unsigned int i;
> +	unsigned int j;
> +
> +	for (i = 0; i < count; i++) {
> +		struct pipe_arg *pipe = &pipes[i];
> +
> +		if (pipe->mode == NULL)
> +			continue;
> +
> +		for (j = 0; j < pipe->num_cons; ++j)
> +			add_property(dev, pipe->con_ids[j], "CRTC_ID",0);
> +
> +		add_property(dev, pipe->crtc->crtc->crtc_id, "MODE_ID", 0);
> +		add_property(dev, pipe->crtc->crtc->crtc_id, "ACTIVE", 0);
> +	}
> +
> +}
> +
> +#define min(a, b)	((a) < (b) ? (a) : (b))
> +
> +static int parse_connector(struct pipe_arg *pipe, const char *arg)
> +{
> +	unsigned int len;
> +	unsigned int i;
> +	const char *p;
> +	char *endp;
> +
> +	pipe->vrefresh = 0;
> +	pipe->crtc_id = (uint32_t)-1;
> +	strcpy(pipe->format_str, "XR24");
> +
> +	/* Count the number of connectors and allocate them. */
> +	pipe->num_cons = 1;
> +	for (p = arg; *p && *p != ':' && *p != '@'; ++p) {
> +		if (*p == ',')
> +			pipe->num_cons++;
> +	}
> +
> +	pipe->con_ids = calloc(pipe->num_cons, sizeof(*pipe->con_ids));
> +	pipe->cons = calloc(pipe->num_cons, sizeof(*pipe->cons));
> +	if (pipe->con_ids == NULL || pipe->cons == NULL)
> +		return -1;
> +
> +	/* Parse the connectors. */
> +	for (i = 0, p = arg; i < pipe->num_cons; ++i, p = endp + 1) {
> +		endp = strpbrk(p, ",@:");
> +		if (!endp)
> +			break;
> +
> +		pipe->cons[i] = strndup(p, endp - p);
> +
> +		if (*endp != ',')
> +			break;
> +	}
> +
> +	if (i != pipe->num_cons - 1)
> +		return -1;
> +
> +	/* Parse the remaining parameters. */
> +	if (*endp == '@') {
> +		arg = endp + 1;
> +		pipe->crtc_id = strtoul(arg, &endp, 10);
> +	}
> +	if (*endp != ':')
> +		return -1;
> +
> +	arg = endp + 1;
> +
> +	/* Search for the vertical refresh or the format. */
> +	p = strpbrk(arg, "-@");
> +	if (p == NULL)
> +		p = arg + strlen(arg);
> +	len = min(sizeof pipe->mode_str - 1, (unsigned int)(p - arg));
> +	strncpy(pipe->mode_str, arg, len);
> +	pipe->mode_str[len] = '\0';
> +
> +	if (*p == '-') {
> +		pipe->vrefresh = strtoul(p + 1, &endp, 10);
> +		p = endp;
> +	}
> +
> +	if (*p == '@') {
> +		strncpy(pipe->format_str, p + 1, 4);
> +		pipe->format_str[4] = '\0';
> +	}
> +
> +	pipe->fourcc = util_format_fourcc(pipe->format_str);
> +	if (pipe->fourcc == 0)  {
> +		fprintf(stderr, "unknown format %s\n", pipe->format_str);
> +		return -1;
> +	}
> +
> +	return 0;
> +}
> +
> +static int parse_plane(struct plane_arg *plane, const char *p)
> +{
> +	char *end;
> +
> +	plane->plane_id = strtoul(p, &end, 10);
> +	if (*end != '@')
> +		return -EINVAL;
> +
> +	p = end + 1;
> +	plane->crtc_id = strtoul(p, &end, 10);
> +	if (*end != ':')
> +		return -EINVAL;
> +
> +	p = end + 1;
> +	plane->w = strtoul(p, &end, 10);
> +	if (*end != 'x')
> +		return -EINVAL;
> +
> +	p = end + 1;
> +	plane->h = strtoul(p, &end, 10);
> +
> +	if (*end == '+' || *end == '-') {
> +		plane->x = strtol(end, &end, 10);
> +		if (*end != '+' && *end != '-')
> +			return -EINVAL;
> +		plane->y = strtol(end, &end, 10);
> +
> +		plane->has_position = true;
> +	}
> +
> +	if (*end == '*') {
> +		p = end + 1;
> +		plane->scale = strtod(p, &end);
> +		if (plane->scale <= 0.0)
> +			return -EINVAL;
> +	} else {
> +		plane->scale = 1.0;
> +	}
> +
> +	if (*end == '@') {
> +		p = end + 1;
> +		if (strlen(p) != 4)
> +			return -EINVAL;
> +
> +		strcpy(plane->format_str, p);
> +	} else {
> +		strcpy(plane->format_str, "XR24");
> +	}
> +
> +	plane->fourcc = util_format_fourcc(plane->format_str);
> +	if (plane->fourcc == 0) {
> +		fprintf(stderr, "unknown format %s\n", plane->format_str);
> +		return -EINVAL;
> +	}
> +
> +	return 0;
> +}
> +
> +static int parse_property(struct property_arg *p, const char *arg)
> +{
> +	if (sscanf(arg, "%d:%32[^:]:%" SCNu64, &p->obj_id, p->name, &p->value) != 3)
> +		return -1;
> +
> +	p->obj_type = 0;
> +	p->name[DRM_PROP_NAME_LEN] = '\0';
> +
> +	return 0;
> +}
> +
> +static void usage(char *name)
> +{
> +	fprintf(stderr, "usage: %s [-cDdefMPpsCvw]\n", name);
> +
> +	fprintf(stderr, "\n Query options:\n\n");
> +	fprintf(stderr, "\t-c\tlist connectors\n");
> +	fprintf(stderr, "\t-e\tlist encoders\n");
> +	fprintf(stderr, "\t-f\tlist framebuffers\n");
> +	fprintf(stderr, "\t-p\tlist CRTCs and planes (pipes)\n");
> +
> +	fprintf(stderr, "\n Test options:\n\n");
> +	fprintf(stderr, "\t-P <plane_id>@<crtc_id>:<w>x<h>[+<x>+<y>][*<scale>][@<format>]\tset a plane\n");
> +	fprintf(stderr, "\t-s <connector_id>[,<connector_id>][@<crtc_id>]:<mode>[-<vrefresh>][@<format>]\tset a mode\n");
> +	fprintf(stderr, "\t-v\ttest loop\n");
> +	fprintf(stderr, "\t-w <obj_id>:<prop_name>:<value>\tset property\n");
> +
> +	fprintf(stderr, "\n Generic options:\n\n");
> +	fprintf(stderr, "\t-d\tdrop master after mode set\n");
> +	fprintf(stderr, "\t-M module\tuse the given driver\n");
> +	fprintf(stderr, "\t-D device\tuse the given device\n");
> +
> +	fprintf(stderr, "\n\tDefault is to dump all info.\n");
> +	exit(0);
> +}
> +
> +static int pipe_resolve_connectors(struct device *dev, struct pipe_arg *pipe)
> +{
> +	drmModeConnector *connector;
> +	unsigned int i;
> +	uint32_t id;
> +	char *endp;
> +
> +	for (i = 0; i < pipe->num_cons; i++) {
> +		id = strtoul(pipe->cons[i], &endp, 10);
> +		if (endp == pipe->cons[i]) {
> +			connector = get_connector_by_name(dev, pipe->cons[i]);
> +			if (!connector) {
> +				fprintf(stderr, "no connector named '%s'\n",
> +					pipe->cons[i]);
> +				return -ENODEV;
> +			}
> +
> +			id = connector->connector_id;
> +		}
> +
> +		pipe->con_ids[i] = id;
> +	}
> +
> +	return 0;
> +}
> +
> +static char optstr[] = "cdD:efM:P:ps:vw:";
> +
> +int main(int argc, char **argv)
> +{
> +	struct device dev;
> +
> +	int c;
> +	int encoders = 0, connectors = 0, crtcs = 0, planes = 0, framebuffers = 0;
> +	int drop_master = 0;
> +	int test_loop = 0;
> +	char *device = NULL;
> +	char *module = NULL;
> +	unsigned int i;
> +	unsigned int count = 0, plane_count = 0;
> +	unsigned int prop_count = 0;
> +	struct pipe_arg *pipe_args = NULL;
> +	struct plane_arg *plane_args = NULL;
> +	struct property_arg *prop_args = NULL;
> +	unsigned int args = 0;
> +	int ret;
> +
> +	memset(&dev, 0, sizeof dev);
> +
> +	opterr = 0;
> +	while ((c = getopt(argc, argv, optstr)) != -1) {
> +		args++;
> +
> +		switch (c) {
> +		case 'c':
> +			connectors = 1;
> +			break;
> +		case 'D':
> +			device = optarg;
> +			args--;
> +			break;
> +		case 'd':
> +			drop_master = 1;
> +			break;
> +		case 'e':
> +			encoders = 1;
> +			break;
> +		case 'f':
> +			framebuffers = 1;
> +			break;
> +		case 'M':
> +			module = optarg;
> +			/* Preserve the default behaviour of dumping all information. */
> +			args--;
> +			break;
> +		case 'P':
> +			plane_args = realloc(plane_args,
> +					     (plane_count + 1) * sizeof *plane_args);
> +			if (plane_args == NULL) {
> +				fprintf(stderr, "memory allocation failed\n");
> +				return 1;
> +			}
> +			memset(&plane_args[plane_count], 0, sizeof(*plane_args));
> +
> +			if (parse_plane(&plane_args[plane_count], optarg) < 0)
> +				usage(argv[0]);
> +
> +			plane_count++;
> +			break;
> +		case 'p':
> +			crtcs = 1;
> +			planes = 1;
> +			break;
> +		case 's':
> +			pipe_args = realloc(pipe_args,
> +					    (count + 1) * sizeof *pipe_args);
> +			if (pipe_args == NULL) {
> +				fprintf(stderr, "memory allocation failed\n");
> +				return 1;
> +			}
> +			memset(&pipe_args[count], 0, sizeof(*pipe_args));
> +
> +			if (parse_connector(&pipe_args[count], optarg) < 0)
> +				usage(argv[0]);
> +
> +			count++;
> +			break;
> +		case 'v':
> +			test_loop = 1;
> +			break;
> +		case 'w':
> +			prop_args = realloc(prop_args,
> +					   (prop_count + 1) * sizeof *prop_args);
> +			if (prop_args == NULL) {
> +				fprintf(stderr, "memory allocation failed\n");
> +				return 1;
> +			}
> +			memset(&prop_args[prop_count], 0, sizeof(*prop_args));
> +
> +			if (parse_property(&prop_args[prop_count], optarg) < 0)
> +				usage(argv[0]);
> +
> +			prop_count++;
> +			break;
> +		default:
> +			usage(argv[0]);
> +			break;
> +		}
> +	}
> +
> +	if (!args)
> +		encoders = connectors = crtcs = planes = framebuffers = 1;
> +
> +	dev.fd = util_open(device, module);
> +	if (dev.fd < 0)
> +		return -1;
> +
> +	ret = drmSetClientCap(dev.fd, DRM_CLIENT_CAP_ATOMIC, 1);
> +	if (ret) {
> +		fprintf(stderr, "no atomic modesetting support: %s\n", strerror(errno));
> +		drmClose(dev.fd);
> +		return -1;
> +	}
> +
> +	dev.resources = get_resources(&dev);
> +	if (!dev.resources) {
> +		drmClose(dev.fd);
> +		return 1;
> +	}
> +
> +	for (i = 0; i < count; i++) {
> +		if (pipe_resolve_connectors(&dev, &pipe_args[i]) < 0) {
> +			free_resources(dev.resources);
> +			drmClose(dev.fd);
> +			return 1;
> +		}
> +	}
> +
> +#define dump_resource(dev, res) if (res) dump_##res(dev)
> +
> +	dump_resource(&dev, encoders);
> +	dump_resource(&dev, connectors);
> +	dump_resource(&dev, crtcs);
> +	dump_resource(&dev, planes);
> +	dump_resource(&dev, framebuffers);
> +
> +	dev.req = drmModeAtomicAlloc();
> +
> +	for (i = 0; i < prop_count; ++i)
> +		set_property(&dev, &prop_args[i]);
> +
> +	if (count && plane_count) {
> +		uint64_t cap = 0;
> +
> +		ret = drmGetCap(dev.fd, DRM_CAP_DUMB_BUFFER, &cap);
> +		if (ret || cap == 0) {
> +			fprintf(stderr, "driver doesn't support the dumb buffer API\n");
> +			drmModeAtomicFree(dev.req);
> +			return 1;
> +		}
> +
> +		set_mode(&dev, pipe_args, count);
> +		set_planes(&dev, plane_args, plane_count, false);
> +
> +		ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
> +		if (ret) {
> +			fprintf(stderr, "Atomic Commit failed [1]\n");
> +			return 1;
> +		}
> +
> +		gettimeofday(&pipe_args->start, NULL);
> +		pipe_args->swap_count = 0;
> +
> +		while (test_loop) {
> +			drmModeAtomicFree(dev.req);
> +			dev.req = drmModeAtomicAlloc();
> +			set_planes(&dev, plane_args, plane_count, true);
> +
> +			ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
> +			if (ret) {
> +				fprintf(stderr, "Atomic Commit failed [2]\n");
> +				return 1;
> +			}
> +
> +			pipe_args->swap_count++;
> +			if (pipe_args->swap_count == 60) {
> +				struct timeval end;
> +				double t;
> +
> +				gettimeofday(&end, NULL);
> +				t = end.tv_sec + end.tv_usec * 1e-6 -
> +				    (pipe_args->start.tv_sec + pipe_args->start.tv_usec * 1e-6);
> +				fprintf(stderr, "freq: %.02fHz\n", pipe_args->swap_count / t);
> +				pipe_args->swap_count = 0;
> +				pipe_args->start = end;
> +			}
> +		}
> +
> +		if (drop_master)
> +			drmDropMaster(dev.fd);
> +
> +		getchar();
> +
> +		drmModeAtomicFree(dev.req);
> +		dev.req = drmModeAtomicAlloc();
> +
> +		clear_mode(&dev, pipe_args, count);
> +		clear_planes(&dev, plane_args, plane_count);
> +		ret = drmModeAtomicCommit(dev.fd, dev.req, DRM_MODE_ATOMIC_ALLOW_MODESET, NULL);
> +		if (ret) {
> +			fprintf(stderr, "Atomic Commit failed\n");
> +			return 1;
> +		}
> +
> +		clear_FB(&dev, plane_args, plane_count);
> +	}
> +
> +	drmModeAtomicFree(dev.req);
> +	free_resources(dev.resources);
> +
> +	return 0;
> +}
> -- 
> 2.15.0
> 
> _______________________________________________
> dri-devel mailing list
> dri-devel@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/dri-devel
_______________________________________________
dri-devel mailing list
dri-devel@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/dri-devel

^ permalink raw reply

* Re: [PATCH 0/5] Misc Coccinelle-related improvements
From: René Scharfe @ 2018-07-23 16:30 UTC (permalink / raw)
  To: SZEDER Gábor, Junio C Hamano
  Cc: git, Derrick Stolee, Nguyễn Thái Ngọc Duy
In-Reply-To: <20180723135100.24288-1-szeder.dev@gmail.com>

Am 23.07.2018 um 15:50 schrieb SZEDER Gábor:
> Just a couple of minor Coccinelle-related improvements:
> 
>    - The first two patches are small cleanups.
> 
>    - The last three could make life perhaps just a tad bit easier for
>      devs running 'make coccicheck'.
> 
> 
> SZEDER Gábor (5):
>    coccinelle: mark the 'coccicheck' make target as .PHONY
>    coccinelle: use $(addsuffix) in 'coccicheck' make target
>    coccinelle: exclude sha1dc source files from static analysis
>    coccinelle: put sane filenames into output patches
>    coccinelle: extract dedicated make target to clean Coccinelle's
>      results

These patches look good to me.  Thanks a lot!

René

^ permalink raw reply

* Re: [Qemu-devel] [PATCH] block/file-posix: add bdrv_attach_aio_context callback for host dev and cdrom
From: Stefan Hajnoczi @ 2018-07-23 16:30 UTC (permalink / raw)
  To: Farhan Ali
  Cc: Nishanth Aravamudan, Eric Blake, Kevin Wolf, John Snow, Max Reitz,
	Fam Zheng, Paolo Bonzini, qemu-block, qemu-devel
In-Reply-To: <a1269390-a798-9165-aaf5-a2aa6270bd54@linux.ibm.com>

[-- Attachment #1: Type: text/plain, Size: 1955 bytes --]

On Fri, Jul 20, 2018 at 03:11:14PM -0400, Farhan Ali wrote:
> I am seeing another issue pop up, in a different test. Even though it's a
> different assertion, it might be related based on the call trace.

Which test case?

> Stack trace of thread 276199:
> #0  0x000003ff8473e274 raise (libc.so.6)
> #1  0x000003ff847239a8 abort (libc.so.6)
> #2  0x000003ff847362ce __assert_fail_base (libc.so.6)
> #3  0x000003ff8473634c __assert_fail (libc.so.6)
> #4  0x000002aa30aba0c4 iov_memset (qemu-system-s390x)
> #5  0x000002aa30aba9a6 qemu_iovec_memset (qemu-system-s390x)
> #6  0x000002aa30a23e88 qemu_laio_process_completion (qemu-system-s390x)

What are the values of laiocb->qiov->size and laiocb->ret?

> #7  0x000002aa30a23f68 qemu_laio_process_completions (qemu-system-s390x)
> #8  0x000002aa30a2418e qemu_laio_process_completions_and_submit
> (qemu-system-s390x)
> #9  0x000002aa30a24220 qemu_laio_poll_cb (qemu-system-s390x)
> #10 0x000002aa30ab22c4 run_poll_handlers_once (qemu-system-s390x)
> #11 0x000002aa30ab2e78 aio_poll (qemu-system-s390x)
> #12 0x000002aa30a29f4e bdrv_do_drained_begin (qemu-system-s390x)
> #13 0x000002aa30a2a276 bdrv_drain (qemu-system-s390x)
> #14 0x000002aa309d45aa bdrv_set_aio_context (qemu-system-s390x)
> #15 0x000002aa3085acfe virtio_blk_data_plane_stop (qemu-system-s390x)
> #16 0x000002aa3096994c virtio_bus_stop_ioeventfd.part.1 (qemu-system-s390x)
> #17 0x000002aa3087d1d6 virtio_vmstate_change (qemu-system-s390x)
> #18 0x000002aa308e8a12 vm_state_notify (qemu-system-s390x)
> #19 0x000002aa3080ed54 do_vm_stop (qemu-system-s390x)
> #20 0x000002aa307bea04 main (qemu-system-s390x)
> #21 0x000003ff84723dd2 __libc_start_main (libc.so.6)
> #22 0x000002aa307c0414 _start (qemu-system-s390x)
> 
> 
> The failing assertion is:
> 
> qemu-kvm: util/iov.c:78: iov_memset: Assertion `offset == 0' failed.

I wonder if the offset is beyond the end of the iovecs.

Thanks,
Stefan

[-- Attachment #2: signature.asc --]
[-- Type: application/pgp-signature, Size: 455 bytes --]

^ permalink raw reply

* Re: [RFC][PATCH 0/5] Mount, Filesystem and Keyrings notifications
From: Casey Schaufler @ 2018-07-23 16:31 UTC (permalink / raw)
  To: David Howells, viro
  Cc: linux-fsdevel, linux-kernel, raven, keyrings,
	linux-security-module
In-Reply-To: <153235954191.32640.5792167066538704794.stgit@warthog.procyon.org.uk>

On 7/23/2018 8:25 AM, David Howells wrote:
> Hi Al,
>
> Here's a set of patches to add a general variable-length notification queue
> concept and to add sources of events for:

Overall I approve. The interface is a bit clunky. Some concerns below.

>
>  (1) Mount topology and reconfiguration change events.

With the possibility of unprivileged mounting you're
going to have to address access control on events.
If root in a user namespace mounts a filesystem you
may have a case where the "real" user wouldn't want the
listener to receive a notification.

>  (2) Superblocks EIO, ENOSPC and EDQUOT events (not complete yet).

Here, too. If SELinux (for example) policy says you can't see
anything on a filesystem you shouldn't get notifications about
things that happen to that filesystem.

>  (3) Key/keyring changes events

And again, I should only get notifications about keys and
keyrings I have access to.

I expect that you intentionally left off

   (4) User injected events

at this point, but it's an obvious extension. That is going
to require access controls (remember kdbus) so I think you'd
do well to design them in now rather than have some security
module hack like me come along later and "fix" it. 

> One of the reasons for this is so that we can remove the issue of processes
> having to repeatedly and regularly scan /proc/mounts, which has proven to be a
> system performance problem.
>
>
> Design decisions:
>
>  (1) A misc chardev is used to create and open a ring buffer:
>
> 	fd = open("/dev/watch_queue", O_RDWR);
>
>      which is then configured and mmap'd into userspace:
>
> 	ioctl(fd, IOC_WATCH_QUEUE_SET_SIZE, BUF_SIZE);
> 	ioctl(fd, IOC_WATCH_QUEUE_SET_FILTER, &filter);
> 	buf = mmap(NULL, BUF_SIZE * page_size, PROT_READ | PROT_WRITE,
> 		   MAP_SHARED, fd, 0);
>
>      The fd cannot be read or written (though there is a facility to use write
>      to inject records for debugging) and userspace just pulls data directly
>      out of the buffer.
>
>  (2) The ring index pointers are stored inside the ring and are thus
>      accessible to userspace.  Userspace should only update the tail pointer
>      and never the head pointer or risk breaking the buffer.  The kernel
>      checks that the pointers appear valid before trying to use them.  A
>      'skip' record is maintained around the pointers.
>
>  (3) poll() can be used to wait for data to appear in the buffer.
>
>  (4) Records in the buffer are binary, typed and have a length so that they
>      can be of varying size.
>
>      This means that multiple heterogeneous sources can share a common
>      buffer.  Tags may be specified when a watchpoint is created to help
>      distinguish the sources.
>
>  (5) The queue is reusable as there are 16 million types available, of which
>      I've used 4, so there is scope for others to be used.
>
>  (6) Records are filterable as types have up to 256 subtypes that can be
>      individually filtered.  Other filtration is also available.
>
>  (7) Each time the buffer is opened, a new buffer is created - this means that
>      there's no interference between watchers.
>
>  (8) When recording a notification, the kernel will not sleep, but will rather
>      mark a queue as overrun if there's insufficient space, thereby avoiding
>      userspace causing the kernel to hang.
>
>  (9) The 'watchpoint' should be specific where possible, meaning that you
>      specify the object that you want to watch.
>
> (10) The buffer is created and then watchpoints are attached to it, using one
>      of:
>
> 	keyctl_watch_key(KEY_SPEC_SESSION_KEYRING, fd, 0x01);
> 	mount_notify(AT_FDCWD, "/", 0, fd, 0x02);
> 	sb_notify(AT_FDCWD, "/mnt", 0, fd, 0x03);
>
>      where in all three cases, fd indicates the queue and the number after is
>      a tag between 0 and 255.
>
> (11) The watch must be removed if either the watch buffer is destroyed or the
>      watched object is destroyed.
>
>
> Things I want to avoid:
>
>  (1) Introducing features that make the core VFS dependent on the network
>      stack or networking namespaces (ie. usage of netlink).
>
>  (2) Dumping all this stuff into dmesg and having a daemon that sits there
>      parsing the output and distributing it as this then puts the
>      responsibility for security into userspace and makes handling namespaces
>      tricky.  Further, dmesg might not exist or might be inaccessible inside a
>      container.
>
>  (3) Letting users see events they shouldn't be able to see.
>
>
> Further things that need to be done:
>
>  (1) fsinfo() syscall needs to find superblocks by ID as well as by path so
>      that it can query a superblock for information without the need to try
>      and work out how to reach it - if the calling process even can.
>
>  (2) A mount_info() syscall is needed that can enumerate all the children of a
>      mount.  This is necessary because mountpoints can hide each other by
>      stacking, so paths are not unique keys.  This will require the ability to
>      look up a mount by ID.  This avoids the need to parse /proc/mounts.
>
>  (3) A keyctl call is needed to allow a watch on a keyring to be extended to
>      "children" of that keyring, such that the watch is removed from the child
>      if it is unlinked from the keyring.
>
>  (4) A global superblock event queue maybe?
>
>  (5) Propagating watches to child superblock over automounts?
>
>
> The patches can be found here also:
>
> 	http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git/log/?h=notifications
>
> David
> ---
> David Howells (5):
>       General notification queue with user mmap()'able ring buffer
>       KEYS: Add a notification facility
>       vfs: Add a mount-notification facility
>       vfs: Add superblock notifications
>       Add sample notification program
>
>
>  Documentation/security/keys/core.rst   |   59 ++
>  Documentation/watch_queue.rst          |  305 ++++++++++++
>  arch/x86/entry/syscalls/syscall_32.tbl |    2 
>  arch/x86/entry/syscalls/syscall_64.tbl |    2 
>  drivers/misc/Kconfig                   |    9 
>  drivers/misc/Makefile                  |    1 
>  drivers/misc/watch_queue.c             |  835 ++++++++++++++++++++++++++++++++
>  fs/Kconfig                             |   21 +
>  fs/Makefile                            |    1 
>  fs/fs_context.c                        |    1 
>  fs/mount.h                             |   26 +
>  fs/mount_notify.c                      |  178 +++++++
>  fs/namespace.c                         |   18 +
>  fs/super.c                             |  116 ++++
>  include/linux/dcache.h                 |    1 
>  include/linux/fs.h                     |   77 +++
>  include/linux/key.h                    |    4 
>  include/linux/syscalls.h               |    4 
>  include/linux/watch_queue.h            |   87 +++
>  include/uapi/linux/keyctl.h            |    1 
>  include/uapi/linux/watch_queue.h       |  156 ++++++
>  kernel/sys_ni.c                        |    6 
>  mm/interval_tree.c                     |    2 
>  mm/memory.c                            |    1 
>  samples/Kconfig                        |    6 
>  samples/Makefile                       |    2 
>  samples/watch_queue/Makefile           |    9 
>  samples/watch_queue/watch_test.c       |  232 +++++++++
>  security/keys/Kconfig                  |   10 
>  security/keys/compat.c                 |    3 
>  security/keys/gc.c                     |    5 
>  security/keys/internal.h               |   29 +
>  security/keys/key.c                    |   37 +
>  security/keys/keyctl.c                 |   90 +++
>  security/keys/keyring.c                |   17 -
>  security/keys/request_key.c            |    4 
>  36 files changed, 2332 insertions(+), 25 deletions(-)
>  create mode 100644 Documentation/watch_queue.rst
>  create mode 100644 drivers/misc/watch_queue.c
>  create mode 100644 fs/mount_notify.c
>  create mode 100644 include/linux/watch_queue.h
>  create mode 100644 include/uapi/linux/watch_queue.h
>  create mode 100644 samples/watch_queue/Makefile
>  create mode 100644 samples/watch_queue/watch_test.c
>
>


^ permalink raw reply

* [RFC][PATCH 0/5] Mount, Filesystem and Keyrings notifications
From: Casey Schaufler @ 2018-07-23 16:31 UTC (permalink / raw)
  To: linux-security-module
In-Reply-To: <153235954191.32640.5792167066538704794.stgit@warthog.procyon.org.uk>

On 7/23/2018 8:25 AM, David Howells wrote:
> Hi Al,
>
> Here's a set of patches to add a general variable-length notification queue
> concept and to add sources of events for:

Overall I approve. The interface is a bit clunky. Some concerns below.

>
>  (1) Mount topology and reconfiguration change events.

With the possibility of unprivileged mounting you're
going to have to address access control on events.
If root in a user namespace mounts a filesystem you
may have a case where the "real" user wouldn't want the
listener to receive a notification.

>  (2) Superblocks EIO, ENOSPC and EDQUOT events (not complete yet).

Here, too. If SELinux (for example) policy says you can't see
anything on a filesystem you shouldn't get notifications about
things that happen to that filesystem.

>  (3) Key/keyring changes events

And again, I should only get notifications about keys and
keyrings I have access to.

I expect that you intentionally left off

   (4) User injected events

at this point, but it's an obvious extension. That is going
to require access controls (remember kdbus) so I think you'd
do well to design them in now rather than have some security
module hack like me come along later and "fix" it. 

> One of the reasons for this is so that we can remove the issue of processes
> having to repeatedly and regularly scan /proc/mounts, which has proven to be a
> system performance problem.
>
>
> Design decisions:
>
>  (1) A misc chardev is used to create and open a ring buffer:
>
> 	fd = open("/dev/watch_queue", O_RDWR);
>
>      which is then configured and mmap'd into userspace:
>
> 	ioctl(fd, IOC_WATCH_QUEUE_SET_SIZE, BUF_SIZE);
> 	ioctl(fd, IOC_WATCH_QUEUE_SET_FILTER, &filter);
> 	buf = mmap(NULL, BUF_SIZE * page_size, PROT_READ | PROT_WRITE,
> 		   MAP_SHARED, fd, 0);
>
>      The fd cannot be read or written (though there is a facility to use write
>      to inject records for debugging) and userspace just pulls data directly
>      out of the buffer.
>
>  (2) The ring index pointers are stored inside the ring and are thus
>      accessible to userspace.  Userspace should only update the tail pointer
>      and never the head pointer or risk breaking the buffer.  The kernel
>      checks that the pointers appear valid before trying to use them.  A
>      'skip' record is maintained around the pointers.
>
>  (3) poll() can be used to wait for data to appear in the buffer.
>
>  (4) Records in the buffer are binary, typed and have a length so that they
>      can be of varying size.
>
>      This means that multiple heterogeneous sources can share a common
>      buffer.  Tags may be specified when a watchpoint is created to help
>      distinguish the sources.
>
>  (5) The queue is reusable as there are 16 million types available, of which
>      I've used 4, so there is scope for others to be used.
>
>  (6) Records are filterable as types have up to 256 subtypes that can be
>      individually filtered.  Other filtration is also available.
>
>  (7) Each time the buffer is opened, a new buffer is created - this means that
>      there's no interference between watchers.
>
>  (8) When recording a notification, the kernel will not sleep, but will rather
>      mark a queue as overrun if there's insufficient space, thereby avoiding
>      userspace causing the kernel to hang.
>
>  (9) The 'watchpoint' should be specific where possible, meaning that you
>      specify the object that you want to watch.
>
> (10) The buffer is created and then watchpoints are attached to it, using one
>      of:
>
> 	keyctl_watch_key(KEY_SPEC_SESSION_KEYRING, fd, 0x01);
> 	mount_notify(AT_FDCWD, "/", 0, fd, 0x02);
> 	sb_notify(AT_FDCWD, "/mnt", 0, fd, 0x03);
>
>      where in all three cases, fd indicates the queue and the number after is
>      a tag between 0 and 255.
>
> (11) The watch must be removed if either the watch buffer is destroyed or the
>      watched object is destroyed.
>
>
> Things I want to avoid:
>
>  (1) Introducing features that make the core VFS dependent on the network
>      stack or networking namespaces (ie. usage of netlink).
>
>  (2) Dumping all this stuff into dmesg and having a daemon that sits there
>      parsing the output and distributing it as this then puts the
>      responsibility for security into userspace and makes handling namespaces
>      tricky.  Further, dmesg might not exist or might be inaccessible inside a
>      container.
>
>  (3) Letting users see events they shouldn't be able to see.
>
>
> Further things that need to be done:
>
>  (1) fsinfo() syscall needs to find superblocks by ID as well as by path so
>      that it can query a superblock for information without the need to try
>      and work out how to reach it - if the calling process even can.
>
>  (2) A mount_info() syscall is needed that can enumerate all the children of a
>      mount.  This is necessary because mountpoints can hide each other by
>      stacking, so paths are not unique keys.  This will require the ability to
>      look up a mount by ID.  This avoids the need to parse /proc/mounts.
>
>  (3) A keyctl call is needed to allow a watch on a keyring to be extended to
>      "children" of that keyring, such that the watch is removed from the child
>      if it is unlinked from the keyring.
>
>  (4) A global superblock event queue maybe?
>
>  (5) Propagating watches to child superblock over automounts?
>
>
> The patches can be found here also:
>
> 	http://git.kernel.org/cgit/linux/kernel/git/dhowells/linux-fs.git/log/?h=notifications
>
> David
> ---
> David Howells (5):
>       General notification queue with user mmap()'able ring buffer
>       KEYS: Add a notification facility
>       vfs: Add a mount-notification facility
>       vfs: Add superblock notifications
>       Add sample notification program
>
>
>  Documentation/security/keys/core.rst   |   59 ++
>  Documentation/watch_queue.rst          |  305 ++++++++++++
>  arch/x86/entry/syscalls/syscall_32.tbl |    2 
>  arch/x86/entry/syscalls/syscall_64.tbl |    2 
>  drivers/misc/Kconfig                   |    9 
>  drivers/misc/Makefile                  |    1 
>  drivers/misc/watch_queue.c             |  835 ++++++++++++++++++++++++++++++++
>  fs/Kconfig                             |   21 +
>  fs/Makefile                            |    1 
>  fs/fs_context.c                        |    1 
>  fs/mount.h                             |   26 +
>  fs/mount_notify.c                      |  178 +++++++
>  fs/namespace.c                         |   18 +
>  fs/super.c                             |  116 ++++
>  include/linux/dcache.h                 |    1 
>  include/linux/fs.h                     |   77 +++
>  include/linux/key.h                    |    4 
>  include/linux/syscalls.h               |    4 
>  include/linux/watch_queue.h            |   87 +++
>  include/uapi/linux/keyctl.h            |    1 
>  include/uapi/linux/watch_queue.h       |  156 ++++++
>  kernel/sys_ni.c                        |    6 
>  mm/interval_tree.c                     |    2 
>  mm/memory.c                            |    1 
>  samples/Kconfig                        |    6 
>  samples/Makefile                       |    2 
>  samples/watch_queue/Makefile           |    9 
>  samples/watch_queue/watch_test.c       |  232 +++++++++
>  security/keys/Kconfig                  |   10 
>  security/keys/compat.c                 |    3 
>  security/keys/gc.c                     |    5 
>  security/keys/internal.h               |   29 +
>  security/keys/key.c                    |   37 +
>  security/keys/keyctl.c                 |   90 +++
>  security/keys/keyring.c                |   17 -
>  security/keys/request_key.c            |    4 
>  36 files changed, 2332 insertions(+), 25 deletions(-)
>  create mode 100644 Documentation/watch_queue.rst
>  create mode 100644 drivers/misc/watch_queue.c
>  create mode 100644 fs/mount_notify.c
>  create mode 100644 include/linux/watch_queue.h
>  create mode 100644 include/uapi/linux/watch_queue.h
>  create mode 100644 samples/watch_queue/Makefile
>  create mode 100644 samples/watch_queue/watch_test.c
>
>

--
To unsubscribe from this list: send the line "unsubscribe linux-security-module" in
the body of a message to majordomo at vger.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html

^ permalink raw reply

* [PATCH] drm/amdgpu/pm: Fix potential Spectre v1
From: Gustavo A. R. Silva @ 2018-07-23 16:32 UTC (permalink / raw)
  To: Alex Deucher, Christian König, David (ChunMing) Zhou,
	David Airlie
  Cc: amd-gfx, dri-devel, linux-kernel, Gustavo A. R. Silva

idx can be indirectly controlled by user-space, hence leading to a
potential exploitation of the Spectre variant 1 vulnerability.

This issue was detected with the help of Smatch:

drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c:408 amdgpu_set_pp_force_state()
warn: potential spectre issue 'data.states'

Fix this by sanitizing idx before using it to index data.states

Notice that given that speculation windows are large, the policy is
to kill the speculation on the first load and not worry if it can be
completed with a dependent load/store [1].

[1] https://marc.info/?l=linux-kernel&m=152449131114778&w=2

Cc: stable@vger.kernel.org
Signed-off-by: Gustavo A. R. Silva <gustavo@embeddedor.com>
---
 drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c
index 15a1192..a446c7c 100644
--- a/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c
+++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_pm.c
@@ -31,7 +31,7 @@
 #include <linux/power_supply.h>
 #include <linux/hwmon.h>
 #include <linux/hwmon-sysfs.h>
-
+#include <linux/nospec.h>
 
 static int amdgpu_debugfs_pm_init(struct amdgpu_device *adev);
 
@@ -403,6 +403,7 @@ static ssize_t amdgpu_set_pp_force_state(struct device *dev,
 			count = -EINVAL;
 			goto fail;
 		}
+		idx = array_index_nospec(idx, ARRAY_SIZE(data.states));
 
 		amdgpu_dpm_get_pp_num_states(adev, &data);
 		state = data.states[idx];
-- 
2.7.4

^ permalink raw reply related

* Re: [Bridge] [PATCH net-next v3 0/2] net: bridge: add support for backup port
From: David Miller @ 2018-07-23 16:32 UTC (permalink / raw)
  To: nikolay; +Cc: netdev, roopa, bridge, wkok, anuradhak
In-Reply-To: <20180723081659.21986-1-nikolay@cumulusnetworks.com>

From: Nikolay Aleksandrov <nikolay@cumulusnetworks.com>
Date: Mon, 23 Jul 2018 11:16:57 +0300

> This set introduces a new bridge port option that allows any port to have
> any other port (in the same bridge of course) as its backup and traffic
> will be forwarded to the backup port when the primary goes down. This is
> mainly used in MLAG and EVPN setups where we have peerlink path which is
> a backup of many (or even all) ports and is a participating bridge port
> itself. There's more detailed information in patch 02. Patch 01 just
> prepares the port sysfs code for options that take raw value. The main
> issues that this set solves are scalability and fallback latency.
 ...

Series applied, thanks Nikolay.

^ permalink raw reply

* Re: [PATCH 00/14] format-patch: add --interdiff and --range-diff options
From: Duy Nguyen @ 2018-07-23 16:32 UTC (permalink / raw)
  To: Eric Sunshine
  Cc: Git Mailing List, Johannes Schindelin,
	Ævar Arnfjörð Bjarmason, Stefan Beller
In-Reply-To: <20180722095717.17912-1-sunshine@sunshineco.com>

On Sun, Jul 22, 2018 at 11:57 AM Eric Sunshine <sunshine@sunshineco.com> wrote:
>
> When re-submitting a patch series, it is often helpful (for reviewers)
> to include an interdiff or range-diff against the previous version.
> Doing so requires manually running git-diff or git-range-diff and
> copy/pasting the result into the cover letter of the new version.
>
> This series automates the process by introducing git-format-patch
> options --interdiff and --range-diff which insert such a diff into the
> cover-letter or into the commentary section of the lone patch of a
> 1-patch series. In the latter case, the interdiff or range-diff is
> indented to avoid confusing git-am and human readers.

I gave up after 10/14. But what I've seen is nice (yes I have a couple
comments here and there but you probably won't need to update
anything).
-- 
Duy

^ permalink raw reply

* RE: fio max blocksize
From: Jeff Furlong @ 2018-07-23 16:33 UTC (permalink / raw)
  To: Jens Axboe, fio@vger.kernel.org
In-Reply-To: <6fae3201-41af-e59e-1f4b-72e17a39f6c2@kernel.dk>

> Please test current -git and ensure that it works properly for you.
Ran latest git (fio-3.8-5-g464b) and tests pass.  Thanks.

Regards,
Jeff




^ permalink raw reply

* Re: [PATCH 0/4][RFC v2] Introduce the in-kernel hibernation encryption
From: Yu Chen @ 2018-07-23 16:38 UTC (permalink / raw)
  To: Pavel Machek
  Cc: Oliver Neukum, Rafael J . Wysocki, Eric Biggers, Lee, Chun-Yi,
	Theodore Ts o, Stephan Mueller, Denis Kenzior, linux-pm,
	linux-crypto, linux-kernel, Gu, Kookoo, Zhang, Rui
In-Reply-To: <20180723122227.GA30092@amd>

Hello,
On Mon, Jul 23, 2018 at 02:22:27PM +0200, Pavel Machek wrote:
> Hi!
> 
> > > > 2. Ideally kernel memory should be encrypted by the
> > > >    kernel itself. We have uswsusp to support user
> > > >    space hibernation, however doing the encryption
> > > >    in kernel space has more advantages:
> > > >    2.1 Not having to transfer plain text kernel memory to
> > > >        user space. Per Lee, Chun-Yi, uswsusp is disabled
> > > >        when the kernel is locked down:
> > > >        https://git.kernel.org/pub/scm/linux/kernel/git/dhowells/
> > > >        linux-fs.git/commit/?h=lockdown-20180410&
> > > >        id=8732c1663d7c0305ae01ba5a1ee4d2299b7b4612
> > > >        due to:
> > > >        "There have some functions be locked-down because
> > > >        there have no appropriate mechanisms to check the
> > > >        integrity of writing data."
> > > >        https://patchwork.kernel.org/patch/10476751/
> > > 
> > > So your goal is to make hibernation compatible with kernel
> > > lockdown? Do your patches provide sufficient security that hibernation
> > > can be enabled with kernel lockdown?
> > 
> > OK, maybe I am dense, but if the key comes from user space, will that
> > be enough?
> 
> Yes, that seems to be one of problems of Yu Chen's patchset.
> 
It is a trade off to derive the key in user space, we once
tried to derive the key in user space, and people suggested
a better way is to do it in user space. And there is a similar
user case of kernel using key from user space is derived from ecryptfs
for ext4.
> > > > Joey Lee and I had a discussion on his previous work at
> > > > https://patchwork.kernel.org/patch/10476751
> > > > We collaborate on this task and his snapshot signature
> > > > feature can be based on this patch set.
> > > 
> > > Well, his work can also work without your patchset, right?
> > 
> > Yes. But you are objecting to encryption in kernel space at all,
> > aren't you?
> 
> I don't particulary love the idea of doing hibernation encryption in
> the kernel, correct.
> 
> But we have this weird thing called secure boot, some people seem to
> want. So we may need some crypto in the kernel -- but I'd like
> something that works with uswsusp, too. Plus, it is mandatory that
> patch explains what security guarantees they want to provide against
> what kinds of attacks...
> 
> Lee, Chun-Yi's patch seemed more promising.				Pavel
> 
The only difference between Chun-Yi's hibernation encrytion solution
and our solution is that his strategy encrypts the snapshot from sratch, 
and ours encryts each page before them going to block device. The benefit
of his solution is that the snapshot can be encrypt in kernel first
thus the uswsusp is allowed  to read it to user space even kernel
is lock down. And I had a discussion with Chun-Yi that we can use
his snapshot solution to make uswsusp happy, and we share the crypto
help code and he can also use our user provided key for his signature.
From this point of view, our code are actually the same, except that
we can help clean up the code and also enhance some encrytion process
for his solution. I don't know why you don't like encryption in kernel, 
because from my point of view, without encryption hibernation in kernel,
uswsusp could not be enabled if kernel is lock down : -) Or do I miss something?
Best,
Yu
> -- 
> (english) http://www.livejournal.com/~pavelmachek
> (cesky, pictures) http://atrey.karlin.mff.cuni.cz/~pavel/picture/horses/blog.html



^ permalink raw reply

* data corruption with 'splt' workload to XFS on DM cache with its 3 underlying devices being on same NVMe device
From: Mike Snitzer @ 2018-07-23 16:33 UTC (permalink / raw)
  To: linux-nvme, linux-block, dm-devel

Hi,

I've opened the following public BZ:
https://bugzilla.redhat.com/show_bug.cgi?id=1607527

Feel free to add comments to that BZ if you have a redhat bugzilla
account.

But otherwise, happy to get as much feedback and discussion going purely
on the relevant lists.  I've taken ~1.5 weeks to categorize and isolate
this issue.  But I've reached a point where I'm getting diminishing
returns and could _really_ use the collective eyeballs and expertise of
the community.  This is by far one of the most nasty cases of corruption
I've seen in a while.  Not sure where the ultimate cause of corruption
lies (that the money question) but it _feels_ rooted in NVMe and is
unique to this particular workload I've stumbled onto via customer
escalation and then trying to replicate an rbd device using a more
approachable one (request-based DM multipath in this case).

>From the BZ's comment#0:

The following occurs with latest v4.18-rc3 and v4.18-rc6 and also occurs
with v4.15.  When corruption occurs from this test it also destroys the
DOS partition table (created during step 0 below).. yeah, corruption is
_that_ bad.  Almost like the corruption is temporal (recently accessed
regions of the NVMe device)?

Anyway: I stumbled onto rampant corruption when using request-based DM
multipath ontop of an NVMe device (not exclusive to a particular drive
either, happens to NVMe devices from multiple vendors).  But the
corruption only occurs if the request-based multipath IO is issued to an
NVMe device in parallel to other IO issued to the _same_ underlying NVMe
by the DM cache target.  See topology detailed below (at the very end of
this comment).. basically all 3 devices that are used to create a DM
cache device need to be backed by the same NVMe device (via partitions
or linear volumes).

Again, using request-based DM multipath for dm-cache's "slow" device is
_required_ to reproduce.  Not 100% clear why really... other than
request-based DM multipath builds large IOs (due to merging).

--- Additional comment from Mike Snitzer on 2018-07-20 10:14:09 EDT ---

To reproduce this issue using device-mapper-test-suite:

0) Partition an NVMe device.  First primary partition with at least a
5GB, seconf primary partition with at least 48GB.
NOTE: larger partitions (e.g. 1: 50GB 2: >= 220GB) can be used to
reproduce XFS corruption much quicker.

1) create a request-based multipath device ontop of an NVMe device,
e.g.:

#!/bin/sh

modprobe dm-service-time

DEVICE=/dev/nvme1n1p2
SIZE=`blockdev --getsz $DEVICE`

echo "0 $SIZE multipath 2 queue_mode mq 0 1 1 service-time 0 1 2 $DEVICE
1000 1" | dmsetup create nvme_mpath

# Just a note for how to fail/reinstate path:
# dmsetup message nvme_mpath 0 "fail_path $DEVICE"
# dmsetup message nvme_mpath 0 "reinstate_path $DEVICE"

2) checkout device-mapper-test-suite from my github repo:

git clone git://github.com/snitm/device-mapper-test-suite.git
cd device-mapper-test-suite
git checkout -b devel origin/devel

3) follow device-mapper-test-suite's README.md to get it all setup

4) Configure /root/.dmtest/config with something like:

profile :nvme_shared do
   metadata_dev '/dev/nvme1n1p1'
   #data_dev '/dev/nvme1n1p2'
   data_dev '/dev/mapper/nvme_mpath'
end

default_profile :nvme_shared

------
NOTE: configured 'metadata_dev' gets carved up by
device-mapper-test-suite to provide both the dm-cache's metadata device
and the "fast" data device.  The configured 'data_dev' is used for
dm-cache's "slow" data device.

5) run the test:
# tail -f /var/log/messages &
# time dmtest run --suite cache -n /split_large_file/

6) If multipath device failed the lone NVMe path you'll need to
reinstate the path before the next iteration of your test, e.g. (from #1
above):
 dmsetup message nvme_mpath 0 "reinstate_path $DEVICE"

--- Additional comment from Mike Snitzer on 2018-07-20 12:02:45 EDT ---

(In reply to Mike Snitzer from comment #6)

> SO seems pretty clear something is still wrong with request-based DM
> multipath ontop of NVMe... sadly we don't have any negative check in
> blk-core, NVMe or elsewhere to offer any clue :(

Building on this comment:

"Anyway, fact that I'm getting this corruption on multiple different
NVMe drives: I am definitely concerned that this BZ is due to a bug
somewhere in NVMe core (or block core code that is specific to NVMe)."

I'm left thinking that request-based DM multipath is somehow causing
NVMe's SG lists or other infrastructure to be "wrong" and it is
resulting in corruption.  I get corruption to the dm-cache's metadata
device (which while theoretically unrelated as its a separate device
from the "slow" dm-cache data device) if the dm-cache slow data device
is backed by request-based dm-multipath ontop of NVMe (which is a
partition from the _same_ NVMe device that is used by the dm-cache
metadata device).

Basically I'm back to thinking NVMe is corrupting the data due to the IO
pattern or nature of the cloned requests dm-multipath is issuing.  And
it is causing corruption to other NVMe partitions on the same parent
NVMe device.  Certainly that is a concerning hypothesis but I'm not
seeing much else that would explain this weird corruption.

If I don't use the same NVMe device (with multiple partitions) for _all_
3 sub-devices that dm-cache needs I don't see the corruption.  It is
almost like the mix of IO issued by DM cache's metadata (on nvme1n1p1
using dm-linear) and "fast" device (also on nvme1n1p1 via dm-linear
volume) in conjunction with IO issued by request-based DM multipath to
NVMe for "slow" device (on nvme1n1p2) is triggering NVMe to respond
negatively.  But this same observation can be made on completely
different hardware using 2 totally different NVMe devices:
testbed1: Intel Corporation Optane SSD 900P Series (2700)
testbed2: Samsung Electronics Co Ltd NVMe SSD Controller 171X (rev 03)

Which is why it feels like some bug in Linux (be it dm-rq.c, blk-core.c,
blk-merge.c or the common NVMe driver)

topology before starting the device-mapper-test-suite test:

# lsblk /dev/nvme1n1
NAME           MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
nvme1n1        259:1    0 745.2G  0 disk
├─nvme1n1p2    259:5    0 695.2G  0 part
│ └─nvme_mpath 253:2    0 695.2G  0 dm
└─nvme1n1p1    259:4    0    50G  0 part

topology during the device-mapper-test-suite test:

# lsblk /dev/nvme1n1
NAME                    MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
nvme1n1                 259:1    0 745.2G  0 disk
├─nvme1n1p2             259:5    0 695.2G  0 part
│ └─nvme_mpath          253:2    0 695.2G  0 dm
│   └─test-dev-458572   253:5    0    48G  0 dm
│     └─test-dev-613083 253:6    0    48G  0 dm
/root/snitm/git/device-mapper-test-suite/kernel_builds
└─nvme1n1p1             259:4    0    50G  0 part
  ├─test-dev-126378     253:4    0     4G  0 dm
  │ └─test-dev-613083   253:6    0    48G  0 dm
  /root/snitm/git/device-mapper-test-suite/kernel_builds
  └─test-dev-652491     253:3    0    40M  0 dm
    └─test-dev-613083   253:6    0    48G  0 dm
    /root/snitm/git/device-mapper-test-suite/kernel_builds

pruning that tree a bit (removing the dm-cache device 253:6) for
clarity:

# lsblk /dev/nvme1n1
NAME                    MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
nvme1n1                 259:1    0 745.2G  0 disk
├─nvme1n1p2             259:5    0 695.2G  0 part
│ └─nvme_mpath          253:2    0 695.2G  0 dm
│   └─test-dev-458572   253:5    0    48G  0 dm
└─nvme1n1p1             259:4    0    50G  0 part
  ├─test-dev-126378     253:4    0     4G  0 dm
  └─test-dev-652491     253:3    0    40M  0 dm

40M device is dm-cache "metadata" device
4G device is dm-cache "fast" data device
48G device is dm-cache "slow" data device

--
dm-devel mailing list
dm-devel@redhat.com
https://www.redhat.com/mailman/listinfo/dm-devel

^ permalink raw reply

* data corruption with 'splt' workload to XFS on DM cache with its 3 underlying devices being on same NVMe device
From: Mike Snitzer @ 2018-07-23 16:33 UTC (permalink / raw)


Hi,

I've opened the following public BZ:
https://bugzilla.redhat.com/show_bug.cgi?id=1607527

Feel free to add comments to that BZ if you have a redhat bugzilla
account.

But otherwise, happy to get as much feedback and discussion going purely
on the relevant lists.  I've taken ~1.5 weeks to categorize and isolate
this issue.  But I've reached a point where I'm getting diminishing
returns and could _really_ use the collective eyeballs and expertise of
the community.  This is by far one of the most nasty cases of corruption
I've seen in a while.  Not sure where the ultimate cause of corruption
lies (that the money question) but it _feels_ rooted in NVMe and is
unique to this particular workload I've stumbled onto via customer
escalation and then trying to replicate an rbd device using a more
approachable one (request-based DM multipath in this case).

>From the BZ's comment#0:

The following occurs with latest v4.18-rc3 and v4.18-rc6 and also occurs
with v4.15.  When corruption occurs from this test it also destroys the
DOS partition table (created during step 0 below).. yeah, corruption is
_that_ bad.  Almost like the corruption is temporal (recently accessed
regions of the NVMe device)?

Anyway: I stumbled onto rampant corruption when using request-based DM
multipath ontop of an NVMe device (not exclusive to a particular drive
either, happens to NVMe devices from multiple vendors).  But the
corruption only occurs if the request-based multipath IO is issued to an
NVMe device in parallel to other IO issued to the _same_ underlying NVMe
by the DM cache target.  See topology detailed below (at the very end of
this comment).. basically all 3 devices that are used to create a DM
cache device need to be backed by the same NVMe device (via partitions
or linear volumes).

Again, using request-based DM multipath for dm-cache's "slow" device is
_required_ to reproduce.  Not 100% clear why really... other than
request-based DM multipath builds large IOs (due to merging).

--- Additional comment from Mike Snitzer on 2018-07-20 10:14:09 EDT ---

To reproduce this issue using device-mapper-test-suite:

0) Partition an NVMe device.  First primary partition with at least a
5GB, seconf primary partition with at least 48GB.
NOTE: larger partitions (e.g. 1: 50GB 2: >= 220GB) can be used to
reproduce XFS corruption much quicker.

1) create a request-based multipath device ontop of an NVMe device,
e.g.:

#!/bin/sh

modprobe dm-service-time

DEVICE=/dev/nvme1n1p2
SIZE=`blockdev --getsz $DEVICE`

echo "0 $SIZE multipath 2 queue_mode mq 0 1 1 service-time 0 1 2 $DEVICE
1000 1" | dmsetup create nvme_mpath

# Just a note for how to fail/reinstate path:
# dmsetup message nvme_mpath 0 "fail_path $DEVICE"
# dmsetup message nvme_mpath 0 "reinstate_path $DEVICE"

2) checkout device-mapper-test-suite from my github repo:

git clone git://github.com/snitm/device-mapper-test-suite.git
cd device-mapper-test-suite
git checkout -b devel origin/devel

3) follow device-mapper-test-suite's README.md to get it all setup

4) Configure /root/.dmtest/config with something like:

profile :nvme_shared do
   metadata_dev '/dev/nvme1n1p1'
   #data_dev '/dev/nvme1n1p2'
   data_dev '/dev/mapper/nvme_mpath'
end

default_profile :nvme_shared

------
NOTE: configured 'metadata_dev' gets carved up by
device-mapper-test-suite to provide both the dm-cache's metadata device
and the "fast" data device.  The configured 'data_dev' is used for
dm-cache's "slow" data device.

5) run the test:
# tail -f /var/log/messages &
# time dmtest run --suite cache -n /split_large_file/

6) If multipath device failed the lone NVMe path you'll need to
reinstate the path before the next iteration of your test, e.g. (from #1
above):
 dmsetup message nvme_mpath 0 "reinstate_path $DEVICE"

--- Additional comment from Mike Snitzer on 2018-07-20 12:02:45 EDT ---

(In reply to Mike Snitzer from comment #6)

> SO seems pretty clear something is still wrong with request-based DM
> multipath ontop of NVMe... sadly we don't have any negative check in
> blk-core, NVMe or elsewhere to offer any clue :(

Building on this comment:

"Anyway, fact that I'm getting this corruption on multiple different
NVMe drives: I am definitely concerned that this BZ is due to a bug
somewhere in NVMe core (or block core code that is specific to NVMe)."

I'm left thinking that request-based DM multipath is somehow causing
NVMe's SG lists or other infrastructure to be "wrong" and it is
resulting in corruption.  I get corruption to the dm-cache's metadata
device (which while theoretically unrelated as its a separate device
from the "slow" dm-cache data device) if the dm-cache slow data device
is backed by request-based dm-multipath ontop of NVMe (which is a
partition from the _same_ NVMe device that is used by the dm-cache
metadata device).

Basically I'm back to thinking NVMe is corrupting the data due to the IO
pattern or nature of the cloned requests dm-multipath is issuing.  And
it is causing corruption to other NVMe partitions on the same parent
NVMe device.  Certainly that is a concerning hypothesis but I'm not
seeing much else that would explain this weird corruption.

If I don't use the same NVMe device (with multiple partitions) for _all_
3 sub-devices that dm-cache needs I don't see the corruption.  It is
almost like the mix of IO issued by DM cache's metadata (on nvme1n1p1
using dm-linear) and "fast" device (also on nvme1n1p1 via dm-linear
volume) in conjunction with IO issued by request-based DM multipath to
NVMe for "slow" device (on nvme1n1p2) is triggering NVMe to respond
negatively.  But this same observation can be made on completely
different hardware using 2 totally different NVMe devices:
testbed1: Intel Corporation Optane SSD 900P Series (2700)
testbed2: Samsung Electronics Co Ltd NVMe SSD Controller 171X (rev 03)

Which is why it feels like some bug in Linux (be it dm-rq.c, blk-core.c,
blk-merge.c or the common NVMe driver)

topology before starting the device-mapper-test-suite test:

# lsblk /dev/nvme1n1
NAME           MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
nvme1n1        259:1    0 745.2G  0 disk
??nvme1n1p2    259:5    0 695.2G  0 part
? ??nvme_mpath 253:2    0 695.2G  0 dm
??nvme1n1p1    259:4    0    50G  0 part

topology during the device-mapper-test-suite test:

# lsblk /dev/nvme1n1
NAME                    MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
nvme1n1                 259:1    0 745.2G  0 disk
??nvme1n1p2             259:5    0 695.2G  0 part
? ??nvme_mpath          253:2    0 695.2G  0 dm
?   ??test-dev-458572   253:5    0    48G  0 dm
?     ??test-dev-613083 253:6    0    48G  0 dm
/root/snitm/git/device-mapper-test-suite/kernel_builds
??nvme1n1p1             259:4    0    50G  0 part
  ??test-dev-126378     253:4    0     4G  0 dm
  ? ??test-dev-613083   253:6    0    48G  0 dm
  /root/snitm/git/device-mapper-test-suite/kernel_builds
  ??test-dev-652491     253:3    0    40M  0 dm
    ??test-dev-613083   253:6    0    48G  0 dm
    /root/snitm/git/device-mapper-test-suite/kernel_builds

pruning that tree a bit (removing the dm-cache device 253:6) for
clarity:

# lsblk /dev/nvme1n1
NAME                    MAJ:MIN RM   SIZE RO TYPE MOUNTPOINT
nvme1n1                 259:1    0 745.2G  0 disk
??nvme1n1p2             259:5    0 695.2G  0 part
? ??nvme_mpath          253:2    0 695.2G  0 dm
?   ??test-dev-458572   253:5    0    48G  0 dm
??nvme1n1p1             259:4    0    50G  0 part
  ??test-dev-126378     253:4    0     4G  0 dm
  ??test-dev-652491     253:3    0    40M  0 dm

40M device is dm-cache "metadata" device
4G device is dm-cache "fast" data device
48G device is dm-cache "slow" data device

^ permalink raw reply

* [Intel-wired-lan] [jkirsher/next-queue PATCH 2/2] ixgbe: Refactor queue disable logic to take completion time into account
From: Shannon Nelson @ 2018-07-23 16:34 UTC (permalink / raw)
  To: intel-wired-lan
In-Reply-To: <20180720222934.3568.15842.stgit@ahduyck-green-test.jf.intel.com>

On 7/20/2018 3:29 PM, Alexander Duyck wrote:
> This change is meant to allow us to take completion time into account when
> disabling queues. Previously we were just working with hard coded values
> for how long we should wait. This worked fine for the standard case where
> completion timeout was operating in the 50us to 50ms range, however on
> platforms that have higher completion timeout times this was resulting in
> Rx queues disable messages being displayed as we weren't waiting long
> enough for outstanding Rx DMA completions.
> 
> Signed-off-by: Alexander Duyck <alexander.h.duyck@intel.com>
> ---

[...]

> +
> +void ixgbe_disable_tx(struct ixgbe_adapter *adapter)
> +{
> +	unsigned long wait_delay, delay_interval;
> +	struct ixgbe_hw *hw = &adapter->hw;
> +	int i, wait_loop;
> +	u32 txdctl;
> +
> +	if (ixgbe_removed(hw->hw_addr))
> +		return;
> +
> +	/* disable all enabled Tx queues */
> +	for (i = 0; i < adapter->num_tx_queues; i++) {
> +		struct ixgbe_ring *ring = adapter->tx_ring[i];
> +		u8 reg_idx = ring->reg_idx;
> +
> +		IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), IXGBE_TXDCTL_SWFLSH);
> +	}
> +
> +	/* disable all enabled XDP Tx queues */
> +	for (i = 0; i < adapter->num_xdp_queues; i++) {
> +		struct ixgbe_ring *ring = adapter->xdp_ring[i];
> +		u8 reg_idx = ring->reg_idx;
> +
> +		IXGBE_WRITE_REG(hw, IXGBE_TXDCTL(reg_idx), IXGBE_TXDCTL_SWFLSH);
> +	}
> +
> +	/* If the link is now up there shouldn't be much in the way of

I think you mean "not" up rather than "now" up, which has the opposite 
meaning and could be a bit confusing.

> +	 * pending transactions. Those that are left will be flushed out
> +	 * when the reset logic goes through the flush sequence to clean out
> +	 * the pending Tx transactions.
> +	 */
> +	if (!(IXGBE_READ_REG(hw, IXGBE_LINKS) & IXGBE_LINKS_UP))
> +		goto dma_engine_disable;
> +
> +	/* Determine our minimum delay interval. We will increase this value
> +	 * with each subsequent test. This way if the device returns quickly
> +	 * we should spend as little time as possible waiting, however as
> +	 * the time increases we will wait for larger periods of time.
> +	 *
> +	 * The trick here is that we increase the interval using the
> +	 * following pattern: 1x 3x 5x 7x 9x 11x 13x 15x 17x 19x. The result
> +	 * of that wait is that it totals up to 100x whatever interval we
> +	 * choose. Since our minimum wait is 100us we can just divide the
> +	 * total timeout by 100 to get our minimum delay interval.
> +	 */
> +	delay_interval = ixgbe_get_completion_timeout(adapter) / 100;
> +
> +	wait_loop = IXGBE_MAX_RX_DESC_POLL;
> +	wait_delay = delay_interval;
> +
> +	while (wait_loop--) {
> +		usleep_range(wait_delay, wait_delay + 10);
> +		wait_delay += delay_interval * 2;
> +		txdctl = 0;
> +
> +		/* OR together the reading of all the active TXDCTL registers,
> +		 * and then test the result. We need the disable to complete
> +		 * before we start freeing the memory and invalidating the
> +		 * DMA mappings.
> +		 */
> +		for (i = 0; i < adapter->num_tx_queues; i++) {
> +			struct ixgbe_ring *ring = adapter->tx_ring[i];
> +			u8 reg_idx = ring->reg_idx;
> +
> +			txdctl |= IXGBE_READ_REG(hw, IXGBE_TXDCTL(reg_idx));
> +		}
> +		for (i = 0; i < adapter->num_xdp_queues; i++) {
> +			struct ixgbe_ring *ring = adapter->xdp_ring[i];
> +			u8 reg_idx = ring->reg_idx;
> +
> +			txdctl |= IXGBE_READ_REG(hw, IXGBE_TXDCTL(reg_idx));
> +		}
> +
> +		if (!(txdctl & IXGBE_TXDCTL_ENABLE))
> +			goto dma_engine_disable;
> +	}
> +
> +	e_err(drv,
> +	      "TXDCTL.ENABLE for one or more queues not cleared within the polling period\n");
> +
> +dma_engine_disable:
> +	/* Disable the Tx DMA engine on 82599 and later MAC */
> +	switch (hw->mac.type) {
> +	case ixgbe_mac_82599EB:
> +	case ixgbe_mac_X540:
> +	case ixgbe_mac_X550:
> +	case ixgbe_mac_X550EM_x:
> +	case ixgbe_mac_x550em_a:
> +		IXGBE_WRITE_REG(hw, IXGBE_DMATXCTL,
> +				(IXGBE_READ_REG(hw, IXGBE_DMATXCTL) &
> +				 ~IXGBE_DMATXCTL_TE));
> +		/* fall through */
> +	default:
> +		break;
> +	}

Picky bike-shed thoughts: I realize you're just moving the previously 
existing code, but with the comment "... and later MAC" I would think 
the translation to code puts all the later MACs into the default and 
have the WRITE_REG() as the default action.  This would make it default 
to the correct action for any new MACs if someone forgot to fix up this 
part of the code.

	switch (hw->mac.type) {
	case ixgbe_mac_82598EB:
		break;

	case ixgbe_mac_82599EB:
	case ixgbe_mac_X540:
	case ixgbe_mac_X550:
	case ixgbe_mac_X550EM_x:
	case ixgbe_mac_x550em_a:
	default:
		IXGBE_WRITE_REG(hw, IXGBE_DMATXCTL,
				(IXGBE_READ_REG(hw, IXGBE_DMATXCTL) &
				 ~IXGBE_DMATXCTL_TE));
		break;
	}

Or even simpler,

	if (hw->mac.type != ixgbe_mac_82598EB)
		IXGBE_WRITE_REG(hw, IXGBE_DMATXCTL,
				(IXGBE_READ_REG(hw, IXGBE_DMATXCTL) &
				 ~IXGBE_DMATXCTL_TE));

Okay, I'll stop now.

Cheers,
sln


^ permalink raw reply

* Re: [igt-dev] ✗ Fi.CI.BAT: failure for series starting with [i-g-t,1/4] lib/igt_pm: Make exit handlers signal safe (rev3)
From: Tvrtko Ursulin @ 2018-07-23 16:35 UTC (permalink / raw)
  To: igt-dev, Patchwork, Tvrtko Ursulin
In-Reply-To: <20180723155447.14391.78678@emeril.freedesktop.org>


On 23/07/2018 16:54, Patchwork wrote:
> == Series Details ==
> 
> Series: series starting with [i-g-t,1/4] lib/igt_pm: Make exit handlers signal safe (rev3)
> URL   : https://patchwork.freedesktop.org/series/47052/
> State : failure
> 
> == Summary ==
> 
> = CI Bug Log - changes from CI_DRM_4521 -> IGTPW_1631 =
> 
> == Summary - FAILURE ==
> 
>    Serious unknown changes coming with IGTPW_1631 absolutely need to be
>    verified manually.
>    
>    If you think the reported changes have nothing to do with the changes
>    introduced in IGTPW_1631, please notify your bug team to allow them
>    to document this new failure mode, which will reduce false positives in CI.
> 
>    External URL: https://patchwork.freedesktop.org/api/1.0/series/47052/revisions/3/mbox/
> 
> == Possible new issues ==
> 
>    Here are the unknown changes that may have been introduced in IGTPW_1631:
> 
>    === IGT changes ===
> 
>      ==== Possible regressions ====
> 
>      igt@pm_rpm@basic-rte:
>        fi-bdw-5557u:       PASS -> FAIL +1

Okay there seems to be two PCI devices claimed by snd_hda_intel on some 
platforms and this patch apparently picks the wrong one. So it seems I 
would need some logic to figure out which one is which, and which one we 
actually want to touch. The one tied with i915 I guess. Just figuring 
out how to find it.

Tvrtko

>      
> == Known issues ==
> 
>    Here are the changes found in IGTPW_1631 that come from known issues:
> 
>    === IGT changes ===
> 
>      ==== Issues hit ====
> 
>      igt@drv_module_reload@basic-reload:
>        fi-glk-j4005:       PASS -> DMESG-WARN (fdo#106725, fdo#106248)
> 
>      igt@pm_rpm@basic-rte:
>        fi-hsw-peppy:       PASS -> FAIL (fdo#106539) +1
>        fi-hsw-4770r:       PASS -> FAIL (fdo#106539) +1
>        fi-hsw-4770:        PASS -> FAIL (fdo#106539) +1
> 
>      
>      ==== Possible fixes ====
> 
>      igt@debugfs_test@read_all_entries:
>        fi-snb-2520m:       INCOMPLETE (fdo#103713) -> PASS
> 
>      igt@prime_vgem@basic-fence-flip:
>        fi-ilk-650:         FAIL (fdo#104008) -> PASS
> 
>      
>    fdo#103713 https://bugs.freedesktop.org/show_bug.cgi?id=103713
>    fdo#104008 https://bugs.freedesktop.org/show_bug.cgi?id=104008
>    fdo#106248 https://bugs.freedesktop.org/show_bug.cgi?id=106248
>    fdo#106539 https://bugs.freedesktop.org/show_bug.cgi?id=106539
>    fdo#106725 https://bugs.freedesktop.org/show_bug.cgi?id=106725
> 
> 
> == Participating hosts (47 -> 43) ==
> 
>    Additional (1): fi-bsw-kefka
>    Missing    (5): fi-ctg-p8600 fi-ilk-m540 fi-byt-squawks fi-bsw-cyan fi-hsw-4200u
> 
> 
> == Build changes ==
> 
>      * IGT: IGT_4570 -> IGTPW_1631
> 
>    CI_DRM_4521: a4ebbd84c682fd30edbde6ac0e48d150d4c5c066 @ git://anongit.freedesktop.org/gfx-ci/linux
>    IGTPW_1631: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1631/
>    IGT_4570: 65cdccdc7bcbb791d791aeeeecb784a382110a3c @ git://anongit.freedesktop.org/xorg/app/intel-gpu-tools
> 
> == Logs ==
> 
> For more details see: https://intel-gfx-ci.01.org/tree/drm-tip/IGTPW_1631/issues.html
> _______________________________________________
> igt-dev mailing list
> igt-dev@lists.freedesktop.org
> https://lists.freedesktop.org/mailman/listinfo/igt-dev
> 
_______________________________________________
igt-dev mailing list
igt-dev@lists.freedesktop.org
https://lists.freedesktop.org/mailman/listinfo/igt-dev

^ permalink raw reply

* Re: [PATCH v2 00/19]  Fixes for sched/numa_balancing
From: Srikar Dronamraju @ 2018-07-23 16:29 UTC (permalink / raw)
  To: Peter Zijlstra
  Cc: Ingo Molnar, LKML, Mel Gorman, Rik van Riel, Thomas Gleixner
In-Reply-To: <20180723152147.GE2494@hirez.programming.kicks-ass.net>

* Peter Zijlstra <peterz@infradead.org> [2018-07-23 17:21:47]:

> On Mon, Jul 23, 2018 at 08:09:55AM -0700, Srikar Dronamraju wrote:
> 
> > > >   sched/numa: Stop multiple tasks from moving to the cpu at the same time
> > 
> > This patch has go-ahead from Mel and Rik and no outstanding comments.
> 
> I left it out because it's part of the big xchg() mess.
> 
> In particular:
> 
> +       if (xchg(&rq->numa_migrate_on, 1))
> +               return;
> +
> +       if (env->best_cpu != -1) {
> +               rq = cpu_rq(env->best_cpu);
> +               WRITE_ONCE(rq->numa_migrate_on, 0);
> +       }
> 
> I'm again confused by clearing numa_migrate_on at this point..

First task choose a cpu to swap/migrate, sets the cpu to best_cpu and
also numa_migrate_on.  Next it finds a better cpu to swap/move. Now if
the task is able to move to the better cpu, then it should clear
numa_migrate_on on the previous best_cpu.

If we dont reset numa_migrate_on on finding a better cpu, the
numa_migrate_on stays set for the previous cpu, causing previous cpu to
never be a target of numa balance.


> 
> > > >   sched/numa: Updation of scan period need not be in lock
> > 
> > I didnt see any comments for this apart from an ack from Rik.
> > + It thought it was trivial and shouldnt have any side-effect.
> 
> Oh, my bad I actually have this one.
> 


^ permalink raw reply

* [Intel-wired-lan] [jkirsher-next-queue:dev-queue] BUILD SUCCESS 17927989beff0dca67895983ed9110707a387a07
From: kbuild test robot @ 2018-07-23 16:35 UTC (permalink / raw)
  To: intel-wired-lan

tree/branch: https://git.kernel.org/pub/scm/linux/kernel/git/jkirsher/next-queue.git  dev-queue
branch HEAD: 17927989beff0dca67895983ed9110707a387a07  e1000: ensure to free old tx/rx rings in set_ringparam()

elapsed time: 29m

configs tested: 110

The following configs have been built successfully.
More configs may be tested in the coming days.

x86_64                randconfig-it0-07231322
i386                               tinyconfig
i386                     randconfig-n0-201829
x86_64                 randconfig-x007-201829
x86_64                 randconfig-x003-201829
x86_64                 randconfig-x000-201829
x86_64                 randconfig-x005-201829
x86_64                 randconfig-x004-201829
x86_64                 randconfig-x008-201829
x86_64                 randconfig-x001-201829
x86_64                 randconfig-x009-201829
x86_64                 randconfig-x002-201829
x86_64                 randconfig-x006-201829
powerpc                           allnoconfig
powerpc                             defconfig
powerpc                       ppc64_defconfig
s390                        default_defconfig
i386                     randconfig-i0-201829
i386                     randconfig-i1-201829
alpha                            alldefconfig
arm                          iop32x_defconfig
alpha                               defconfig
parisc                            allnoconfig
parisc                         b180_defconfig
parisc                        c3000_defconfig
parisc                              defconfig
i386                             allmodconfig
x86_64                 randconfig-x010-201829
x86_64                 randconfig-x011-201829
x86_64                 randconfig-x012-201829
x86_64                 randconfig-x013-201829
x86_64                 randconfig-x014-201829
x86_64                 randconfig-x015-201829
x86_64                 randconfig-x016-201829
x86_64                 randconfig-x017-201829
x86_64                 randconfig-x018-201829
x86_64                 randconfig-x019-201829
i386                     randconfig-a0-201829
i386                     randconfig-a1-201829
arm                          lpd270_defconfig
arm                        vexpress_defconfig
arm                            zeus_defconfig
mips                      fuloong2e_defconfig
i386                             alldefconfig
i386                              allnoconfig
i386                                defconfig
m68k                       m5475evb_defconfig
m68k                          multi_defconfig
m68k                           sun3_defconfig
i386                     randconfig-s0-201829
i386                     randconfig-s1-201829
openrisc                    or1ksim_defconfig
um                             i386_defconfig
um                           x86_64_defconfig
i386                   randconfig-x012-201829
i386                   randconfig-x017-201829
i386                   randconfig-x014-201829
i386                   randconfig-x016-201829
i386                   randconfig-x013-201829
i386                   randconfig-x011-201829
i386                   randconfig-x018-201829
i386                   randconfig-x010-201829
i386                   randconfig-x015-201829
i386                   randconfig-x019-201829
i386                   randconfig-x078-201829
i386                   randconfig-x070-201829
i386                   randconfig-x075-201829
i386                   randconfig-x076-201829
i386                   randconfig-x074-201829
i386                   randconfig-x079-201829
i386                   randconfig-x071-201829
i386                   randconfig-x073-201829
i386                   randconfig-x072-201829
i386                   randconfig-x077-201829
sh                                allnoconfig
sh                          rsk7269_defconfig
sh                  sh7785lcr_32bit_defconfig
sh                            titan_defconfig
i386                   randconfig-x008-201829
i386                   randconfig-x009-201829
i386                   randconfig-x005-201829
i386                   randconfig-x000-201829
i386                   randconfig-x003-201829
i386                   randconfig-x001-201829
i386                   randconfig-x004-201829
i386                   randconfig-x006-201829
i386                   randconfig-x007-201829
i386                   randconfig-x002-201829
mips                           32r2_defconfig
mips                         64r6el_defconfig
mips                              allnoconfig
mips                                   jz4740
mips                      malta_kvm_defconfig
mips                                     txx9
microblaze                      mmu_defconfig
microblaze                    nommu_defconfig
arm                               allnoconfig
arm                         at91_dt_defconfig
arm                           efm32_defconfig
arm                          exynos_defconfig
arm                        multi_v5_defconfig
arm                        multi_v7_defconfig
arm                        shmobile_defconfig
arm                           sunxi_defconfig
arm64                             allnoconfig
arm64                               defconfig
x86_64                             acpi-redef
x86_64                           allyesdebian
x86_64                                nfsroot
powerpc                 mpc836x_mds_defconfig

---
0-DAY kernel test infrastructure                Open Source Technology Center
https://lists.01.org/pipermail/kbuild-all                   Intel Corporation

^ permalink raw reply

* Re: [Qemu-devel] [PATCH v3 25/40] target/mips: Add updating CP0 BadInstrX register for nanoMIPs only
From: Richard Henderson @ 2018-07-23 16:35 UTC (permalink / raw)
  To: Stefan Markovic, qemu-devel
  Cc: laurent, riku.voipio, philippe.mathieu.daude, aurelien, amarkovic,
	smarkovic, pjovanovic, pburton
In-Reply-To: <1532004912-13899-26-git-send-email-stefan.markovic@rt-rk.com>

On 07/19/2018 05:54 AM, Stefan Markovic wrote:
> From: Stefan Markovic <smarkovic@wavecomp.com>
> 
> Signed-off-by: Yongbok Kim <yongbok.kim@mips.com>
> Signed-off-by: Aleksandar Markovic <amarkovic@wavecomp.com>
> Signed-off-by: Stefan Markovic <smarkovic@wavecomp.com>
> ---
>  target/mips/helper.c | 6 ++++++
>  1 file changed, 6 insertions(+)
> 
> diff --git a/target/mips/helper.c b/target/mips/helper.c
> index 5299f21..9535131 100644
> --- a/target/mips/helper.c
> +++ b/target/mips/helper.c
> @@ -695,6 +695,12 @@ static inline void set_badinstr_registers(CPUMIPSState *env)
>                  instr |= cpu_lduw_code(env, env->active_tc.PC + 2);
>              }
>              env->CP0_BadInstr = instr;
> +
> +            if ((env->insn_flags & ISA_NANOMIPS32) &&
> +                ((instr & 0xFC000000) == 0x60000000)) {
> +                instr = cpu_lduw_code(env, env->active_tc.PC + 4) << 16;
> +                env->CP0_BadInstrX = instr;
> +            }

The nanomips condition has been checked just above.
This patch should probably be merged with 24/40.


r~

^ permalink raw reply

* Re: [Qemu-devel] [PATCH v3 24/40] target/mips: Add updating BadInstr and BadInstrP registers for nanoMIPS
From: Richard Henderson @ 2018-07-23 16:36 UTC (permalink / raw)
  To: Stefan Markovic, qemu-devel
  Cc: laurent, riku.voipio, philippe.mathieu.daude, aurelien, amarkovic,
	smarkovic, pjovanovic, pburton
In-Reply-To: <1532004912-13899-25-git-send-email-stefan.markovic@rt-rk.com>

On 07/19/2018 05:54 AM, Stefan Markovic wrote:
> +        if (!(env->insn_flags & ISA_NANOMIPS32)) {
> +            /* TODO: add BadInstr support for pre-nanoMIPS */
> +             return;
> +        }
> +        if (env->CP0_Config3 & (1 << CP0C3_BI)) {
> +            instr = (cpu_lduw_code(env, env->active_tc.PC)) << 16;
> +            if ((env->insn_flags & ISA_NANOMIPS32) &&

Redundant check for nanomips.


r~

^ permalink raw reply


This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.