|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH v3 8/9] viridian: add implementation of synthetic timers
>>> On 31.01.19 at 11:47, <paul.durrant@xxxxxxxxxx> wrote:
> --- a/xen/arch/x86/hvm/viridian/synic.c
> +++ b/xen/arch/x86/hvm/viridian/synic.c
> @@ -329,7 +329,53 @@ void viridian_synic_domain_deinit(struct domain *d)
>
> void viridian_synic_poll_messages(struct vcpu *v)
> {
> - /* There are currently no message sources */
> + viridian_time_poll_timers(v);
> +}
> +
> +bool viridian_synic_deliver_timer_msg(struct vcpu *v, unsigned int sintx,
> + unsigned int index,
> + int64_t expiration, int64_t delivery)
> +{
> + const union viridian_sint_msr *vs = &v->arch.hvm.viridian->sint[sintx];
> + HV_MESSAGE *msg = v->arch.hvm.viridian->simp.ptr;
> + struct {
> + uint32_t TimerIndex;
> + uint32_t Reserved;
> + uint64_t ExpirationTime;
> + uint64_t DeliveryTime;
> + } payload = {
> + .TimerIndex = index,
> + .ExpirationTime = expiration,
> + .DeliveryTime = delivery,
> + };
> +
> + if ( test_bit(sintx, &v->arch.hvm.viridian->msg_pending) )
> + return false;
> +
> + BUILD_BUG_ON(sizeof(*msg) != HV_MESSAGE_SIZE);
> + msg += sintx;
> +
> + /*
> + * To avoid using an atomic test-and-set this function must be called
> + * in context of the vcpu receiving the message.
> + */
> + ASSERT(v == current);
> + if ( msg->Header.MessageType != HvMessageTypeNone )
> + {
> + msg->Header.MessageFlags.MessagePending = 1;
> + set_bit(sintx, &v->arch.hvm.viridian->msg_pending);
As per the comment above this is always in context of the subject
vCPU. It looks to me as if this was also the case for the two
clear_bit() on the field in the prior patch. If so, all three could be
the non-atomic variants instead.
> + return false;
> + }
> +
> + msg->Header.MessageType = HvMessageTimerExpired;
> + msg->Header.MessageFlags.MessagePending = 0;
> + msg->Header.PayloadSize = sizeof(payload);
> + memcpy(msg->Payload, &payload, sizeof(payload));
> +
> + if ( !vs->fields.mask )
> + vlapic_set_irq(vcpu_vlapic(v), vs->fields.vector, 0);
If this wasn't with v == current, I think you'd also need a barrier
here. Could you extend the comment above to also mention this
aspect?
> @@ -118,14 +119,237 @@ static int64_t time_ref_count(struct domain *d)
> return raw_trc_val(d) + trc->off;
> }
>
> +static int64_t time_now(struct domain *d)
Why would this return a signed value? And can't the function
parameter be const?
> +{
> + const struct viridian_page *rt = &d->arch.hvm.viridian->reference_tsc;
> + HV_REFERENCE_TSC_PAGE *p = rt->ptr;
> + uint32_t start, end;
> + __int128_t tsc;
> + __int128_t scale;
I don't think you need both of them be 128 bits wide. I also don't
see why either would want to be of a signed type.
> + int64_t offset;
> +
> + /*
> + * If the reference TSC page is not enabled, or has been invalidated
> + * fall back to the partition reference counter.
> + */
> + if ( !p || !p->TscSequence )
> + return time_ref_count(d);
> +
> + /*
> + * The following sampling algorithm for tsc, scale and offset is
> + * documented in the specifiction.
> + */
> + start = p->TscSequence;
> +
> + do {
> + tsc = rdtsc();
> + scale = p->TscScale;
> + offset = p->TscOffset;
> +
> + smp_mb();
> + end = p->TscSequence;
Why is this a full barrier, rather than just a read one? And don't you need
to add a counterpart in update_reference_tsc()?
> + } while (end != start);
update_reference_tsc() increments TscSequence. If end doesn't match
start at this point, you're entering a near infinite loop here as long as
you don't update start inside the loop. I also think that there's a
second read barrier needed between this initial reading of the sequence
number and the reading of the actual values.
> + /*
> + * The specification says: "The partition reference time is computed
> + * by the following formula:
> + *
> + * ReferenceTime = ((VirtualTsc * TscScale) >> 64) + TscOffset
> + *
> + * The multiplication is a 64 bit multiplication, which results in a
> + * 128 bit number which is then shifted 64 times to the right to obtain
> + * the high 64 bits."
> + */
> + return ((tsc * scale) >> 64) + offset;
> +}
> +
> +static void stop_stimer(struct viridian_stimer *vs)
> +{
> + struct vcpu *v = vs->v;
const?
> + unsigned int stimerx = vs - &v->arch.hvm.viridian->stimer[0];
> +
> + if ( !vs->started )
> + return;
> +
> + stop_timer(&vs->timer);
> + clear_bit(stimerx, &v->arch.hvm.viridian->stimer_pending);
> + vs->started = false;
> +}
> +
> +static void stimer_expire(void *data)
> +{
> + struct viridian_stimer *vs = data;
const?
> + struct vcpu *v = vs->v;
> + unsigned int stimerx = vs - &v->arch.hvm.viridian->stimer[0];
> +
> + if ( !vs->config.fields.enabled )
> + return;
> +
> + set_bit(stimerx, &v->arch.hvm.viridian->stimer_pending);
> + vcpu_kick(v);
> +}
> +
> +static void start_stimer(struct viridian_stimer *vs)
> +{
> + struct vcpu *v = vs->v;
> + unsigned int stimerx = vs - &v->arch.hvm.viridian->stimer[0];
> + int64_t now = time_now(v->domain);
> + s_time_t timeout;
> +
> + if ( !test_and_set_bit(stimerx, &v->arch.hvm.viridian->stimer_enabled) )
> + printk(XENLOG_G_INFO "%pv: VIRIDIAN STIMER%u: enabled\n", v,
> + stimerx);
> +
> + if ( vs->config.fields.periodic )
> + {
> + unsigned int missed = 0;
> + int64_t next;
> +
> + /*
> + * The specification says that if the timer is lazy then we
> + * skip over any missed expirations so we can treat this case
> + * as the same as if the timer is currently stopped, i.e. we
> + * just schedule expiration to be 'count' ticks from now.
> + */
> + if ( !vs->started || vs->config.fields.lazy )
> + {
> + next = now + vs->count;
> + }
Unnecessary braces.
> @@ -149,6 +373,57 @@ int viridian_time_wrmsr(struct vcpu *v, uint32_t idx,
> uint64_t val)
> }
> break;
>
> + case HV_X64_MSR_TIME_REF_COUNT:
> + return X86EMUL_EXCEPTION;
Isn't this an unrelated change?
> + case HV_X64_MSR_STIMER0_CONFIG:
> + case HV_X64_MSR_STIMER1_CONFIG:
> + case HV_X64_MSR_STIMER2_CONFIG:
> + case HV_X64_MSR_STIMER3_CONFIG:
> + {
> + unsigned int stimerx = (idx - HV_X64_MSR_STIMER0_CONFIG) / 2;
> + struct viridian_stimer *vs = &v->arch.hvm.viridian->stimer[stimerx];
> +
> + if ( !(viridian_feature_mask(d) & HVMPV_stimer) )
> + return X86EMUL_EXCEPTION;
> +
> + stop_stimer(vs);
> +
> + vs->config.raw = val;
> +
> + if ( !vs->config.fields.sintx )
> + vs->config.fields.enabled = 0;
> +
> + if ( vs->config.fields.enabled )
> + start_stimer(vs);
> +
> + break;
> + }
> + case HV_X64_MSR_STIMER0_COUNT:
Missing blank line again (and also further down here as well as in the
rdmsr code).
> + case HV_X64_MSR_STIMER1_COUNT:
> + case HV_X64_MSR_STIMER2_COUNT:
> + case HV_X64_MSR_STIMER3_COUNT:
> + {
> + unsigned int stimerx = (idx - HV_X64_MSR_STIMER0_COUNT) / 2;
> + struct viridian_stimer *vs = &v->arch.hvm.viridian->stimer[stimerx];
> +
> + if ( !(viridian_feature_mask(d) & HVMPV_stimer) )
> + return X86EMUL_EXCEPTION;
> +
> + stop_stimer(vs);
> +
> + vs->count = val;
> +
> + if ( !vs->count )
Any reason you don't use val here (which the compiler likely will do
anyway)?
> @@ -201,6 +476,32 @@ int viridian_time_rdmsr(const struct vcpu *v, uint32_t
> idx, uint64_t *val)
> break;
> }
>
> + case HV_X64_MSR_STIMER0_CONFIG:
> + case HV_X64_MSR_STIMER1_CONFIG:
> + case HV_X64_MSR_STIMER2_CONFIG:
> + case HV_X64_MSR_STIMER3_CONFIG:
> + {
> + unsigned int stimerx = (idx - HV_X64_MSR_STIMER0_CONFIG) / 2;
> +
> + if ( !(viridian_feature_mask(d) & HVMPV_stimer) )
> + return X86EMUL_EXCEPTION;
> +
> + *val = v->arch.hvm.viridian->stimer[stimerx].config.raw;
While more noticeable here and ...
> + break;
> + }
> + case HV_X64_MSR_STIMER0_COUNT:
> + case HV_X64_MSR_STIMER1_COUNT:
> + case HV_X64_MSR_STIMER2_COUNT:
> + case HV_X64_MSR_STIMER3_COUNT:
> + {
> + unsigned int stimerx = (idx - HV_X64_MSR_STIMER0_COUNT) / 2;
> +
> + if ( !(viridian_feature_mask(d) & HVMPV_stimer) )
> + return X86EMUL_EXCEPTION;
> +
> + *val = v->arch.hvm.viridian->stimer[stimerx].count;
... here, array_access_nospec() are probably needed not just here,
but also in the wrmsr logic.
> --- a/xen/include/asm-x86/hvm/viridian.h
> +++ b/xen/include/asm-x86/hvm/viridian.h
> @@ -40,6 +40,33 @@ union viridian_sint_msr
> } fields;
> };
>
> +union viridian_stimer_config_msr
> +{
> + uint64_t raw;
> + struct
> + {
> + uint64_t enabled:1;
> + uint64_t periodic:1;
> + uint64_t lazy:1;
> + uint64_t auto_enable:1;
> + uint64_t vector:8;
> + uint64_t direct_mode:1;
> + uint64_t reserved_zero1:3;
> + uint64_t sintx:4;
> + uint64_t reserved_zero2:44;
> + } fields;
> +};
> +
> +struct viridian_stimer {
> + struct vcpu *v;
Isn't a full 8-byte pointer a little too much overhead here? You could
instead store the timer index ...
> + struct timer timer;
> + union viridian_stimer_config_msr config;
> + uint64_t count;
> + int64_t expiration;
> + s_time_t timeout;
> + bool started;
... in a field using the 7-byte padding here, and use container_of()
to get at the outer structure.
Jan
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |