|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH V5 2/3] x86/mm: allocate logdirty_ranges for altp2ms
On 11/13/18 7:57 PM, George Dunlap wrote:
> On 11/11/18 2:07 PM, Razvan Cojocaru wrote:
>> This patch is a pre-requisite for the one fixing VGA logdirty
>> freezes when using altp2m. It only concerns itself with the
>> ranges allocation / deallocation / initialization part. While
>> touching the code, I've switched global_logdirty from bool_t
>> to bool.
>>
>> Signed-off-by: Razvan Cojocaru <rcojocaru@xxxxxxxxxxxxxxx>
>
> I've convinced myself that this patch is probably correct now, and as a
> result I've had a chance to look a bit at the resulting code. Which
> means, unfortunately, that I'm going to be a bit annoying and ask more
> questions that I didn't ask last time.
Thanks for the review, and please ask away. :)
>> ---
>> CC: George Dunlap <george.dunlap@xxxxxxxxxxxxx>
>> CC: Jan Beulich <jbeulich@xxxxxxxx>
>> CC: Andrew Cooper <andrew.cooper3@xxxxxxxxxx>
>> CC: Wei Liu <wei.liu2@xxxxxxxxxx>
>>
>> ---
>> Changes since V4:
>> - Always call p2m_free_logdirty() in p2m_free_one() (previously
>> the call was gated on hap_enabled(p2m->domain) && cpu_has_vmx).
>> ---
>> xen/arch/x86/mm/p2m.c | 74
>> ++++++++++++++++++++++++++++++++++++-----------
>> xen/include/asm-x86/p2m.h | 2 +-
>> 2 files changed, 58 insertions(+), 18 deletions(-)
>>
>> diff --git a/xen/arch/x86/mm/p2m.c b/xen/arch/x86/mm/p2m.c
>> index 42b9ef4..69536c1 100644
>> --- a/xen/arch/x86/mm/p2m.c
>> +++ b/xen/arch/x86/mm/p2m.c
>> @@ -59,6 +59,28 @@ static void p2m_nestedp2m_init(struct p2m_domain *p2m)
>> #endif
>> }
>>
>> +static int p2m_init_logdirty(struct p2m_domain *p2m)
>> +{
>> + if ( p2m->logdirty_ranges )
>> + return 0;
>> +
>> + p2m->logdirty_ranges = rangeset_new(p2m->domain, "log-dirty",
>> + RANGESETF_prettyprint_hex);
>> + if ( !p2m->logdirty_ranges )
>> + return -ENOMEM;
>> +
>> + return 0;
>> +}
>> +
>> +static void p2m_free_logdirty(struct p2m_domain *p2m)
>> +{
>> + if ( !p2m->logdirty_ranges )
>> + return;
>> +
>> + rangeset_destroy(p2m->logdirty_ranges);
>> + p2m->logdirty_ranges = NULL;
>> +}
>> +
>> /* Init the datastructures for later use by the p2m code */
>> static int p2m_initialise(struct domain *d, struct p2m_domain *p2m)
>> {
>> @@ -107,6 +129,7 @@ free_p2m:
>>
>> static void p2m_free_one(struct p2m_domain *p2m)
>> {
>> + p2m_free_logdirty(p2m);
>> if ( hap_enabled(p2m->domain) && cpu_has_vmx )
>> ept_p2m_uninit(p2m);
>> free_cpumask_var(p2m->dirty_cpumask);
>> @@ -116,19 +139,19 @@ static void p2m_free_one(struct p2m_domain *p2m)
>> static int p2m_init_hostp2m(struct domain *d)
>> {
>> struct p2m_domain *p2m = p2m_init_one(d);
>> + int rc;
>>
>> - if ( p2m )
>> - {
>> - p2m->logdirty_ranges = rangeset_new(d, "log-dirty",
>> - RANGESETF_prettyprint_hex);
>> - if ( p2m->logdirty_ranges )
>> - {
>> - d->arch.p2m = p2m;
>> - return 0;
>> - }
>> + if ( !p2m )
>> + return -ENOMEM;
>> +
>> + rc = p2m_init_logdirty(p2m);
>> +
>> + if ( !rc )
>> + d->arch.p2m = p2m;
>> + else
>> p2m_free_one(p2m);
>> - }
>> - return -ENOMEM;
>> +
>> + return rc;
>> }
>>
>> static void p2m_teardown_hostp2m(struct domain *d)
>> @@ -138,7 +161,6 @@ static void p2m_teardown_hostp2m(struct domain *d)
>>
>> if ( p2m )
>> {
>> - rangeset_destroy(p2m->logdirty_ranges);
>> p2m_free_one(p2m);
>> d->arch.p2m = NULL;
>> }
>> @@ -2279,6 +2301,18 @@ void p2m_flush_altp2m(struct domain *d)
>> altp2m_list_unlock(d);
>> }
>
> I think everything above here could usefully be in its own patch; it
> would make it easier to verify that there were no functional changes in
> the refactoring.
Right, I'll split this patch then.
>> +static int p2m_init_altp2m_logdirty(struct p2m_domain *p2m)
>> +{
>> + struct p2m_domain *hostp2m = p2m_get_hostp2m(p2m->domain);
>> + int rc = p2m_init_logdirty(p2m);
>> +
>> + if ( rc )
>> + return rc;
>> +
>> + /* The following is really just a rangeset copy. */
>> + return rangeset_merge(p2m->logdirty_ranges, hostp2m->logdirty_ranges);
>> +}
>> +
>> int p2m_init_altp2m_by_id(struct domain *d, unsigned int idx)
>> {
>> int rc = -EINVAL;
>> @@ -2290,8 +2324,9 @@ int p2m_init_altp2m_by_id(struct domain *d, unsigned
>> int idx)
>>
>> if ( d->arch.altp2m_eptp[idx] == mfn_x(INVALID_MFN) )
>> {
>> - p2m_init_altp2m_ept(d, idx);
>> - rc = 0;
>> + rc = p2m_init_altp2m_logdirty(d->arch.altp2m_p2m[idx]);
>> + if ( !rc )
>> + p2m_init_altp2m_ept(d, idx);
>> }
>>
>> altp2m_list_unlock(d);
>> @@ -2310,9 +2345,13 @@ int p2m_init_next_altp2m(struct domain *d, uint16_t
>> *idx)
>> if ( d->arch.altp2m_eptp[i] != mfn_x(INVALID_MFN) )
>> continue;
>>
>> - p2m_init_altp2m_ept(d, i);
>> - *idx = i;
>> - rc = 0;
>> + rc = p2m_init_altp2m_logdirty(d->arch.altp2m_p2m[i]);
>> +
>> + if ( !rc )
>> + {
>> + p2m_init_altp2m_ept(d, i);
>> + *idx = i;
>> + }
>
> It looks like there's a 1-1 correspondence between
> p2m_init_altp2m_logdirty() succeeding and calling p2m_inti_altp2m_ept().
> Would it make sense to combine them into the same function, maybe named
> "p2m_activate_altp2m()" or something (to distinguish it from
> p2m_init_altp2m())?
Of course, that'll cut back on some redundancy. Always a good thing.
>> @@ -2341,6 +2380,7 @@ int p2m_destroy_altp2m_by_id(struct domain *d,
>> unsigned int idx)
>> {
>> p2m_flush_table(d->arch.altp2m_p2m[idx]);
>> /* Uninit and reinit ept to force TLB shootdown */
>> + p2m_free_logdirty(d->arch.altp2m_p2m[idx]);
>> ept_p2m_uninit(d->arch.altp2m_p2m[idx]);
>> ept_p2m_init(d->arch.altp2m_p2m[idx]);
>> d->arch.altp2m_eptp[idx] = mfn_x(INVALID_MFN);
>
> (In case I forget: Also, this is called without holding the appropriate
> p2m lock. )
Could you please provide more details? I have assumed that at the point
of calling a function called p2m_destroy_altp2m_by_id() it should be
safe to tear the altp2m down without further precaution.
I think you're saying that I should p2m_lock(d->arch.altp2m_p2m[idx])
just for the duration of the p2m_free_logdirty() call?
> I'm a bit suspicious of long strings of these sorts of functions in the
> middle of another function. It turns out that there are three copies of
> this sequence of function calls (p2m_flush_table -> ept_p2m_uninit ->
> ept_p2m_init):
>
> * Here (p2m_destroy_altp2m_id), when the user asks for the alt2m index
> to be destroyed
>
> * In p2m_flush_altp2m(), which is called when altp2m is disabled for a
> domain
>
> * In p2m_reset_altp2m(), which is called when an entry in the hostp2m is
> set to INVALID_MFN.
>
> Presumably in p2m_reset_altp2m() we don't want to call
> p2m_free_logdirty(), as the altp2m is still active and we want to keep
> the logdirty ranges around. But in p2m_flush_altp2m(), I'm pretty sure
> we do want to discard them: when altp2m is enabled again,
> p2m_init_logdirty() will return early, leaving the old rangesets in
> place; if the hostp2m rangesets have changed between the time altp2m was
> disabled and enabled again, the rangeset_merge() may have incorrect results.
I'll call p2m_free_logdirty() in p2m_flush_altp2m() as well.
> At the moment we essentially have two "init" states:
> * After domain creation; altp2m structures allocated, but no rangesets, & c
> * After being enabled for the first time: rangesets mirroring hostp2m,
> p2m_init_altp2m_ept() initialization done
>
> Is there any particular reason we allocate the p2m structures on domain
> creation, but not logdirty range structures? It seems like allocating
> altp2m structures on-demand, rather than at domain creation time, might
> make a lot of the reasoning here simpler.
I assume that this question is not addressed to me, since I'm not able
to answer it - I can only assume that having less heap used has been
preferred.
Thanks,
Razvan
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |