diff mbox series

[RFC,3/3] memory: introduce IOMMU_NOTIFIER_USER_[UN]SET

Message ID 20180605131944.14649-4-peterx@redhat.com
State New
Headers show
Series memory: enhance IOMMU notifier to support USER bit | expand

Commit Message

Peter Xu June 5, 2018, 1:19 p.m. UTC
Add two more IOMMU notifier flags to selectively choose whether the
notifier would like to listen to an event that with USER bit set/unset.
Note that all existing notifiers should always been registered with both
of the flags set to make sure they'll receive the notification no matter
whether the USER bit is set or unset in the attributes.  To simplify
this procedure, some new macros are defined.  The old UNMAP-only
notifiers should now be registered with IOMMU_NOTIFIER_UNMAP_ALL
flags (rather than the old IOMMU_NOTIFIER_UNMAP flag), while the old
MAP+UNMAP case can keep to use the IOMMU_NOTIFIER_ALL flag.

Now if a new notifier would like to register to only UNMAP notifications
with USER bit set, it should register with below flag:

  IOMMU_NOTIFIER_UNMAP | IOMMU_NOTIFIER_USER_SET

Then when we want to notify a DMA invalidation (we call it IOMMUTLBEntry
in QEMU), we should do this:

  IOMMUTLBEntry entry;
  ... (set up the fields)
  entry.perm = IOMMU_NONE;
  entry.attrs.user = 1;
  memory_region_notify_iommu(mr, &entry);

Then only the notifiers registered with IOMMU_NOTIFIER_USER_SET will
receive this notification.

Signed-off-by: Peter Xu <peterx@redhat.com>
---
 include/exec/memory.h | 48 ++++++++++++++++++++++++++++++++++++++-----
 hw/virtio/vhost.c     |  2 +-
 memory.c              | 10 +++++++++
 3 files changed, 54 insertions(+), 6 deletions(-)

Comments

Peter Maydell June 5, 2018, 1:54 p.m. UTC | #1
On 5 June 2018 at 14:19, Peter Xu <peterx@redhat.com> wrote:
> Add two more IOMMU notifier flags to selectively choose whether the
> notifier would like to listen to an event that with USER bit set/unset.
> Note that all existing notifiers should always been registered with both
> of the flags set to make sure they'll receive the notification no matter
> whether the USER bit is set or unset in the attributes.  To simplify
> this procedure, some new macros are defined.  The old UNMAP-only
> notifiers should now be registered with IOMMU_NOTIFIER_UNMAP_ALL
> flags (rather than the old IOMMU_NOTIFIER_UNMAP flag), while the old
> MAP+UNMAP case can keep to use the IOMMU_NOTIFIER_ALL flag.
>
> Now if a new notifier would like to register to only UNMAP notifications
> with USER bit set, it should register with below flag:
>
>   IOMMU_NOTIFIER_UNMAP | IOMMU_NOTIFIER_USER_SET
>
> Then when we want to notify a DMA invalidation (we call it IOMMUTLBEntry
> in QEMU), we should do this:
>
>   IOMMUTLBEntry entry;
>   ... (set up the fields)
>   entry.perm = IOMMU_NONE;
>   entry.attrs.user = 1;
>   memory_region_notify_iommu(mr, &entry);

I don't think this works, because there is not a single unique
MemTxAttrs for a particular invalidation event. Invalidates
will affect classes of transaction attributes. For instance
in the MPC, changing the lookup table can invalidate cached
IOMMU information for any translation that was done either
with attrs.secure = 1, or for translations with attrs.unspecified = 1.

In general, at the point where the IOMMU calls notifiers it
won't have a single MemTxAttrs it can use, so you don't want
to put an attrs into the IOMMUTLBEntry.

thanks
-- PMM
Peter Xu June 5, 2018, 2:26 p.m. UTC | #2
On Tue, Jun 05, 2018 at 02:54:18PM +0100, Peter Maydell wrote:
> On 5 June 2018 at 14:19, Peter Xu <peterx@redhat.com> wrote:
> > Add two more IOMMU notifier flags to selectively choose whether the
> > notifier would like to listen to an event that with USER bit set/unset.
> > Note that all existing notifiers should always been registered with both
> > of the flags set to make sure they'll receive the notification no matter
> > whether the USER bit is set or unset in the attributes.  To simplify
> > this procedure, some new macros are defined.  The old UNMAP-only
> > notifiers should now be registered with IOMMU_NOTIFIER_UNMAP_ALL
> > flags (rather than the old IOMMU_NOTIFIER_UNMAP flag), while the old
> > MAP+UNMAP case can keep to use the IOMMU_NOTIFIER_ALL flag.
> >
> > Now if a new notifier would like to register to only UNMAP notifications
> > with USER bit set, it should register with below flag:
> >
> >   IOMMU_NOTIFIER_UNMAP | IOMMU_NOTIFIER_USER_SET
> >
> > Then when we want to notify a DMA invalidation (we call it IOMMUTLBEntry
> > in QEMU), we should do this:
> >
> >   IOMMUTLBEntry entry;
> >   ... (set up the fields)
> >   entry.perm = IOMMU_NONE;
> >   entry.attrs.user = 1;
> >   memory_region_notify_iommu(mr, &entry);
> 
> I don't think this works, because there is not a single unique
> MemTxAttrs for a particular invalidation event. Invalidates
> will affect classes of transaction attributes. For instance
> in the MPC, changing the lookup table can invalidate cached
> IOMMU information for any translation that was done either
> with attrs.secure = 1, or for translations with attrs.unspecified = 1.
> 
> In general, at the point where the IOMMU calls notifiers it
> won't have a single MemTxAttrs it can use, so you don't want
> to put an attrs into the IOMMUTLBEntry.

So we have a requirement that we want to send an invalidation for
either (1) unspecified, or (2) secure=1.  We can't do that with a
single MemTxAttrs.

Could we just notify twice?  One with unspecified=1 and one with
secure=1?  Is this (reduce the calls to notify functions) the major
reason we introduce this IOMMU index idea into the memory API (besides
"avoid possible programming error" you mentioned in IRC discussion)?

Again, I think the more critical question would be whether we can pass
in MemTxAttrs into translate(), and whether we can avoid introducing
this IOMMU index idea into the memory API layer... For example, would
it add complexity to other architectures without much benefit?

Thanks,
Peter Maydell June 5, 2018, 2:42 p.m. UTC | #3
On 5 June 2018 at 15:26, Peter Xu <peterx@redhat.com> wrote:
> So we have a requirement that we want to send an invalidation for
> either (1) unspecified, or (2) secure=1.  We can't do that with a
> single MemTxAttrs.
>
> Could we just notify twice?  One with unspecified=1 and one with
> secure=1?  Is this (reduce the calls to notify functions) the major
> reason we introduce this IOMMU index idea into the memory API (besides
> "avoid possible programming error" you mentioned in IRC discussion)?

How would you handle "this changes mappings for txattrs where
unspecified == 0 && secure == 0" ?

How does the notifier registering API say what it's interested in?
In the exec.c code that deals with sending TCG transactions
through IOMMUs, if I have to register a notifier for every
possible TCG transaction attribute I ever see, that's a lot
of unnecessary notifiers.

> Again, I think the more critical question would be whether we can pass
> in MemTxAttrs into translate(), and whether we can avoid introducing
> this IOMMU index idea into the memory API layer... For example, would
> it add complexity to other architectures without much benefit?

I think the fundamental difference in our points of view here
is that I see the IOMMU index as *reducing* complexity, not
adding it. Yes, it is an extra level of indirection, but I
think it helps us express a useful concept. The patches you've
sent here seem to me to be adding extra complexity to the
notifier API and the core code which isn't required in
the patch set that I sent.

thanks
-- PMM
Peter Xu June 6, 2018, 6:56 a.m. UTC | #4
On Tue, Jun 05, 2018 at 03:42:11PM +0100, Peter Maydell wrote:
> On 5 June 2018 at 15:26, Peter Xu <peterx@redhat.com> wrote:
> > So we have a requirement that we want to send an invalidation for
> > either (1) unspecified, or (2) secure=1.  We can't do that with a
> > single MemTxAttrs.
> >
> > Could we just notify twice?  One with unspecified=1 and one with
> > secure=1?  Is this (reduce the calls to notify functions) the major
> > reason we introduce this IOMMU index idea into the memory API (besides
> > "avoid possible programming error" you mentioned in IRC discussion)?
> 
> How would you handle "this changes mappings for txattrs where
> unspecified == 0 && secure == 0" ?

Let's forget this patch.  I was confused about the problem behind when
I wrote this up...  Now I know a bit more.  It's related to how we can
send a notification covers multiple contexts. Let's assume we still
only support MAP and UNMAP in the notifiers. But let's assume
IOMMUTLBEntry has MemTxAttrs field.

If so, for "this changes mappings for txattrs where unspecified == 0
&& secure == 0", we just send one notify with attrs.unspecified=0 and
attrs.secure=0.  Meanwhile in the notifier hook you can parse the
attrs to see whether that's what you need, and skip if not.

Will that work?

> 
> How does the notifier registering API say what it's interested in?
> In the exec.c code that deals with sending TCG transactions
> through IOMMUs, if I have to register a notifier for every
> possible TCG transaction attribute I ever see, that's a lot
> of unnecessary notifiers.

Again, let's assume this patch does not exist.  Then only one notifier
is needed to be registered with UNMAP notify, and in the hook function
it can detect whether it has (attrs.unspecified==0 &&
attrs.unsecure==0).

> 
> > Again, I think the more critical question would be whether we can pass
> > in MemTxAttrs into translate(), and whether we can avoid introducing
> > this IOMMU index idea into the memory API layer... For example, would
> > it add complexity to other architectures without much benefit?
> 
> I think the fundamental difference in our points of view here
> is that I see the IOMMU index as *reducing* complexity, not
> adding it. Yes, it is an extra level of indirection, but I
> think it helps us express a useful concept.

Yes.  And again, I'm also worrying about what we should do when we
want to pass requester ID into translate() too if we have this extra
layer.

> The patches you've
> sent here seem to me to be adding extra complexity to the
> notifier API and the core code which isn't required in
> the patch set that I sent.

Again I didn't really understand the problem behind before.  Now I
don't think it's important on whether we'll need to introduce new
notifier flags, I wanted to know whether we can avoid introducing
IOMMU index into memory API. Let's just ignore this patch.  Sorry for
that confusion.

Regards,
Peter Xu June 6, 2018, 7:09 a.m. UTC | #5
On Wed, Jun 06, 2018 at 02:56:29PM +0800, Peter Xu wrote:

[...]

> > The patches you've
> > sent here seem to me to be adding extra complexity to the
> > notifier API and the core code which isn't required in
> > the patch set that I sent.
> 
> Again I didn't really understand the problem behind before.  Now I
> don't think it's important on whether we'll need to introduce new
> notifier flags, I wanted to know whether we can avoid introducing
> IOMMU index into memory API. Let's just ignore this patch.  Sorry for
> that confusion.

For this part, I'd say of course this is based on the assumption that
the IOMMU index might not help much with non-tcg use cases.  If
someone could help me understand better on how that could help in
general to memory API then I would be much appreciated, since it
really confused me even until today (and I can't see a good way at
least for VT-d to leverage that idea).  Thanks,
diff mbox series

Patch

diff --git a/include/exec/memory.h b/include/exec/memory.h
index 12865a4890..fb9a7059c6 100644
--- a/include/exec/memory.h
+++ b/include/exec/memory.h
@@ -82,18 +82,56 @@  struct IOMMUTLBEntry {
 };
 
 /*
- * Bitmap for different IOMMUNotifier capabilities. Each notifier can
- * register with one or multiple IOMMU Notifier capability bit(s).
+ * Bitmap for different IOMMUNotifier capabilities.  Please refer to
+ * comments for each notifier capability to know its usage. Note that,
+ * a notifier registered with (UNMAP | USER_SET) does not mean that
+ * it'll notify both MAP notifies and USER_SET notifies, instead it
+ * means it'll only be notified for the events that are UNMAP
+ * meanwhile with USER attribute set.
  */
 typedef enum {
     IOMMU_NOTIFIER_NONE = 0,
-    /* Notify cache invalidations */
+    /*
+     * When set, will notify deleted entries (cache invalidations).
+     * When unset, will not notify deleted entries.
+     */
     IOMMU_NOTIFIER_UNMAP = 0x1,
-    /* Notify entry changes (newly created entries) */
+    /*
+     * When set, will notify newly created entries.  When unset, will
+     * not notify newly created entries.
+     */
     IOMMU_NOTIFIER_MAP = 0x2,
+    /*
+     * When set, will notify when the USER bit is set in
+     * IOMMUTLBEntry.attrs.  When unset, will not notify when the USER
+     * bit is set.
+     */
+    IOMMU_NOTIFIER_USER_SET = 0x4,
+    /*
+     * When set, will notify when the USER bit is cleared in
+     * IOMMUTLBEntry.attrs.  When unset, will not notify when the USER
+     * bit is cleared.
+     */
+    IOMMU_NOTIFIER_USER_UNSET = 0x8,
 } IOMMUNotifierFlag;
 
-#define IOMMU_NOTIFIER_ALL (IOMMU_NOTIFIER_MAP | IOMMU_NOTIFIER_UNMAP)
+/* Use this when the notifier does not care about USER bit */
+#define IOMMU_NOTIFIER_USER_ALL \
+    (IOMMU_NOTIFIER_USER_SET | IOMMU_NOTIFIER_USER_UNSET)
+
+/* Use this when the notifier does not care about any attribute */
+#define IOMMU_NOTIFIER_ATTRS_ALL \
+    (IOMMU_NOTIFIER_USER_ALL)
+
+/* Use this to notify all UNMAP notifications */
+#define IOMMU_NOTIFIER_UNMAP_ALL \
+    (IOMMU_NOTIFIER_UNMAP | IOMMU_NOTIFIER_ATTRS_ALL)
+
+/* Use this to notify all notifications */
+#define IOMMU_NOTIFIER_ALL (                    \
+        IOMMU_NOTIFIER_MAP |                    \
+        IOMMU_NOTIFIER_UNMAP |                  \
+        IOMMU_NOTIFIER_ATTRS_ALL)
 
 struct IOMMUNotifier;
 typedef void (*IOMMUNotify)(struct IOMMUNotifier *notifier,
diff --git a/hw/virtio/vhost.c b/hw/virtio/vhost.c
index 96175b214d..da6efeadad 100644
--- a/hw/virtio/vhost.c
+++ b/hw/virtio/vhost.c
@@ -672,7 +672,7 @@  static void vhost_iommu_region_add(MemoryListener *listener,
                      section->size);
     end = int128_sub(end, int128_one());
     iommu_notifier_init(&iommu->n, vhost_iommu_unmap_notify,
-                        IOMMU_NOTIFIER_UNMAP,
+                        IOMMU_NOTIFIER_UNMAP_ALL,
                         section->offset_within_region,
                         int128_get64(end));
     iommu->mr = section->mr;
diff --git a/memory.c b/memory.c
index 376f72b19c..9e4617df5a 100644
--- a/memory.c
+++ b/memory.c
@@ -1880,6 +1880,16 @@  void memory_region_notify_one(IOMMUNotifier *notifier,
         return;
     }
 
+    if (!(notifier->notifier_flags & IOMMU_NOTIFIER_USER_SET) &&
+        entry->attrs.user) {
+        return;
+    }
+
+    if (!(notifier->notifier_flags & IOMMU_NOTIFIER_USER_UNSET) &&
+        !entry->attrs.user) {
+        return;
+    }
+
     if (entry->perm & IOMMU_RW) {
         request_flags = IOMMU_NOTIFIER_MAP;
     } else {