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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).