From: George Dunlap <george.dunlap@citrix.com>
To: Jan Beulich <JBeulich@suse.com>,
xen-devel <xen-devel@lists.xenproject.org>
Cc: George Dunlap <George.Dunlap@eu.citrix.com>,
Andrew Cooper <andrew.cooper3@citrix.com>,
Kevin Tian <kevin.tian@intel.com>, Keir Fraser <keir@xen.org>,
Jun Nakajima <jun.nakajima@intel.com>
Subject: Re: [PATCH 1/3] x86/p2m: tighten conditions of IOMMU mapping updates
Date: Mon, 28 Sep 2015 17:40:17 +0100 [thread overview]
Message-ID: <56096D71.5050103@citrix.com> (raw)
In-Reply-To: <56096B84.40503@citrix.com>
On 28/09/15 17:32, George Dunlap wrote:
> On 21/09/15 15:02, Jan Beulich wrote:
>> In the EPT case permission changes should also result in updates or
>> TLB flushes.
>>
>> In the NPT case the old MFN does not depend on the new entry being
>> valid (but solely on the old one), and the need to update or TLB-flush
>> again also depends on permission changes.
>>
>> In the shadow mode case, iommu_hap_pt_share should be ignored.
>>
>> Furthermore in the NPT/shadow case old intermediate page tables must
>> be freed only after IOMMU side updates/flushes have got carried out.
>>
>> Signed-off-by: Jan Beulich <jbeulich@suse.com>
>
> So first of all, having all these changes in the same patch almost
> certainly slowed down the review process a bit.
>
>> ---
>> In addition to the fixes here it looks to me as if both EPT and
>> NPT/shadow code lack invalidation of IOMMU side paging structure
>> caches, i.e. further changes may be needed. Am I overlooking something?
>>
>> --- a/xen/arch/x86/mm/p2m-ept.c
>> +++ b/xen/arch/x86/mm/p2m-ept.c
>> @@ -668,6 +668,7 @@ ept_set_entry(struct p2m_domain *p2m, un
>> uint8_t ipat = 0;
>> int need_modify_vtd_table = 1;
>> int vtd_pte_present = 0;
>> + unsigned int iommu_flags = p2m_get_iommu_flags(p2mt);
>> enum { sync_off, sync_on, sync_check } needs_sync = sync_check;
>> ept_entry_t old_entry = { .epte = 0 };
>> ept_entry_t new_entry = { .epte = 0 };
>> @@ -798,8 +799,9 @@ ept_set_entry(struct p2m_domain *p2m, un
>> new_entry.mfn = mfn_x(mfn);
>>
>> /* Safe to read-then-write because we hold the p2m lock */
>> - if ( ept_entry->mfn == new_entry.mfn )
>> - need_modify_vtd_table = 0;
>> + if ( ept_entry->mfn == new_entry.mfn &&
>> + p2m_get_iommu_flags(ept_entry->sa_p2mt) == iommu_flags )
>> + need_modify_vtd_table = 0;
>>
>> ept_p2m_type_to_flags(p2m, &new_entry, p2mt, p2ma);
>> }
>> @@ -830,11 +832,9 @@ out:
>> iommu_pte_flush(d, gfn, &ept_entry->epte, order, vtd_pte_present);
>> else
>> {
>> - unsigned int flags = p2m_get_iommu_flags(p2mt);
>> -
>> - if ( flags != 0 )
>> + if ( iommu_flags )
>> for ( i = 0; i < (1 << order); i++ )
>> - iommu_map_page(d, gfn + i, mfn_x(mfn) + i, flags);
>> + iommu_map_page(d, gfn + i, mfn_x(mfn) + i, iommu_flags);
>> else
>> for ( i = 0; i < (1 << order); i++ )
>> iommu_unmap_page(d, gfn + i);
>
> EPT changes:
>
> Reviewed-by: George Dunlap <george.dunlap@citrix.com>
>
> And now...
>
>> --- a/xen/arch/x86/mm/p2m-pt.c
>> +++ b/xen/arch/x86/mm/p2m-pt.c
>> @@ -488,12 +488,13 @@ p2m_pt_set_entry(struct p2m_domain *p2m,
>> void *table;
>> unsigned long i, gfn_remainder = gfn;
>> l1_pgentry_t *p2m_entry;
>> - l1_pgentry_t entry_content;
>> + l1_pgentry_t entry_content, old_entry = l1e_empty();
>> l2_pgentry_t l2e_content;
>> l3_pgentry_t l3e_content;
>> int rc;
>> unsigned int iommu_pte_flags = p2m_get_iommu_flags(p2mt);
>> - unsigned long old_mfn = 0;
>> + unsigned int old_flags = 0;
>> + unsigned long old_mfn = INVALID_MFN;
>
> I think this could use at least a comment, and perhaps some better
> variable naming here explaining what setting or not setting each of
> these different variables means.
>
> Trying to sort out what the effect of setting old_flags to ~0 is
> particularly convoluted.
>
> Inferring from the code:
>
> 1) Setting old_entry() to some value will cause those values to be
> unmapped. This will only be set for 1G and 2M entries, if the existing
> entry is both present and not set as a superpage. This is pretty
> straightforward and looks correct.
>
> 2) old_mfn and old_flags are primarily used to control whether an IOMMU
> flush happens.
>
> 3) old_mfn
> - old_mfn begins at INVALID_MFN, and is set only if the entry itself is
> leaf entry.
> - a flush will happen if old_mfn != mfn
> - The effect of this will be to force a flush if replacing a leaf with
> a leaf but a different mfn, or if replacing an intermediate table with a
> leaf *if* the leaf is not INVALID_MFN
>
> 4) old_flags
> - old_flags will be the actual flags if replacing a leaf with a leaf,
> or ~0 if replacing an intermediate table with a leaf.
> - a flush will happen if
> p2m_iommu_get_flags(p2m_flags_to_type(old_flags)) != iommu_pte_flags.
> - p2m_flags_to_type
> - returns p2m_invalid if flags == 0. This should never happen here
> - returns the type bits from the flags otherwires.
> - So in the case of old_flags being the actual flags, you get the
> actual type of the old entry
> - in the case of old_flags being ~0, you get 0x7f, which is undefined
> - p2m_iommu_get_flags() will return 0 for unknown types.
> - so the effect of the above will be to force a flush if replacing a
> leaf with a leaf of different iommu permisions; or, to force a flush if
> replacing an intermediate table with any permissions with a leaf that
> has at least some iommu permissions.
>
> Did I get the logic there right?
>
> It *looks* to me, therefore, like this won't handle flushes properly in
> the case of replacing an intermediate table with an empty leaf entry:
> - old_mfn will be INVALID_MFN, matching mfn
> - the result of the function call around old_flags will be 0, matching
> iommu_pte_flags for the new entry (0)
> - so no flush will happen
> - but because you're removing permissions, a flush actually should happen.
>
> Did I miss something?
Regardless whether I did or not, this code is very difficult to reason
about, even after you've figured out what old_mfn and old_flags are
meant to be used for. It seems like maintaining a boolean like
"needs_flush" would be clearer and easier to reason about.
I might rename "old_entry" to "intermediate_table" as well, with a
comment saying "/* Intermediate table to free if we're replacing it with
a superpage */" or something like that.
-George
next prev parent reply other threads:[~2015-09-28 16:40 UTC|newest]
Thread overview: 24+ messages / expand[flat|nested] mbox.gz Atom feed top
2015-09-21 13:57 [PATCH 0/3] x86: further P2M adjustments Jan Beulich
2015-09-21 14:02 ` [PATCH 1/3] x86/p2m: tighten conditions of IOMMU mapping updates Jan Beulich
2015-09-22 14:15 ` Jan Beulich
2015-09-23 14:00 ` Wei Liu
2015-09-29 10:47 ` Jan Beulich
2015-09-29 11:28 ` Wei Liu
2015-09-28 8:55 ` Tian, Kevin
2015-09-28 9:06 ` Jan Beulich
2015-09-28 15:12 ` Jan Beulich
2015-09-28 16:32 ` George Dunlap
2015-09-28 16:40 ` George Dunlap [this message]
2015-09-29 7:34 ` Jan Beulich
2015-09-29 7:58 ` Jan Beulich
2015-09-30 0:02 ` Jiang, Yunhong
2015-09-30 9:58 ` Jan Beulich
2015-10-02 16:37 ` Jiang, Yunhong
2015-09-21 14:03 ` [PATCH 2/3] x86/p2m‑pt: use pre‑calculated IOMMU flags Jan Beulich
2015-09-28 16:42 ` George Dunlap
2015-10-01 16:31 ` Wei Liu
2015-09-21 14:03 ` [PATCH 3/3] x86/p2m‑ept: adjust some types in ept_set_entry() Jan Beulich
2015-09-28 8:56 ` Tian, Kevin
2015-09-28 16:44 ` George Dunlap
2015-09-25 6:58 ` [PATCH 1/3] x86/p2m: tighten conditions of IOMMU mapping updates Jan Beulich
2015-09-28 11:33 ` [PATCH 0/3] x86: further P2M adjustments Jan Beulich
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=56096D71.5050103@citrix.com \
--to=george.dunlap@citrix.com \
--cc=George.Dunlap@eu.citrix.com \
--cc=JBeulich@suse.com \
--cc=andrew.cooper3@citrix.com \
--cc=jun.nakajima@intel.com \
--cc=keir@xen.org \
--cc=kevin.tian@intel.com \
--cc=xen-devel@lists.xenproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
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.