[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: [Xen-devel] [PATCH v14 for-xen-4.5 11/21] x86/VPMU: Interface for setting PMU mode and flags



>>> On 17.10.14 at 23:17, <boris.ostrovsky@xxxxxxxxxx> wrote:
> --- a/xen/arch/x86/hvm/vpmu.c
> +++ b/xen/arch/x86/hvm/vpmu.c
> @@ -21,6 +21,8 @@
>  #include <xen/config.h>
>  #include <xen/sched.h>
>  #include <xen/xenoprof.h>
> +#include <xen/event.h>
> +#include <xen/guest_access.h>
>  #include <asm/regs.h>
>  #include <asm/types.h>
>  #include <asm/msr.h>
> @@ -32,8 +34,11 @@
>  #include <asm/hvm/svm/vmcb.h>
>  #include <asm/apic.h>
>  #include <public/pmu.h>
> +#include <xen/tasklet.h>
> +#include <xsm/xsm.h>

Please put the xen/ one alongside the other xen/ ones.

> +static int vpmu_force_context_switch(void)
> +{
> +    unsigned i, numcpus, mycpu;
> +    static s_time_t start;
> +    struct vcpu *curr_vcpu = current;
> +    static DEFINE_PER_CPU(struct tasklet *, sync_task);
> +    int ret = 0;
> +
> +    numcpus = num_online_cpus();

I think you'd be better off counting these as you schedule the tasklets.

> +    mycpu = smp_processor_id();
> +
> +    if ( sync_vcpu != NULL ) /* if set, we may be in hypercall continuation 
> */
> +    {
> +        if (sync_vcpu != curr_vcpu )

Coding style.

> +            /* We are not the original caller */
> +            return -EAGAIN;

That would seem to be the wrong return value then. Also, the HAP
side fix for XSA-97 taught us that identifying a caller by vCPU is
problematic - in the course of the retries the kernel's scheduler
may move the calling process to a different vCPU, yet it's still the
legitimate original caller.

> +        goto cont_wait;
> +    }
> +
> +    for_each_online_cpu ( i )
> +    {
> +        if ( i == mycpu )
> +            continue;
> +
> +        per_cpu(sync_task, i) = xmalloc(struct tasklet);
> +        if ( per_cpu(sync_task, i) == NULL )
> +        {
> +            printk(XENLOG_WARNING "vpmu_force_context_switch: out of 
> memory\n");
> +            ret = -ENOMEM;
> +            goto out;
> +        }
> +        tasklet_init(per_cpu(sync_task, i), vpmu_sched_checkin, 0);
> +    }
> +
> +    /* First count is for self */
> +    atomic_set(&vpmu_sched_counter, 1);
> +
> +    for_each_online_cpu ( i )
> +    {
> +        if ( i != mycpu )
> +            tasklet_schedule_on_cpu(per_cpu(sync_task, i), i);
> +    }
> +
> +    vpmu_save(current);
> +
> +    sync_vcpu = curr_vcpu;
> +    start = NOW();
> +
> + cont_wait:
> +    /*
> +     * Note that we may fail here if a CPU is hot-plugged while we are
> +     * waiting. We will then time out.
> +     */

And I continue to miss the handling of the hot-unplug case (or at the
very least a note on this being unimplemented [and going to crash],
to at least clarify matters to the curious reader).

> +    while ( atomic_read(&vpmu_sched_counter) != numcpus )
> +    {
> +        s_time_t now;
> +
> +        cpu_relax();
> +
> +        now = NOW();
> +
> +        /* Give up after (arbitrarily chosen) 5 seconds */
> +        if ( now > start + SECONDS(5) )
> +        {
> +            printk(XENLOG_WARNING
> +                   "vpmu_force_context_switch: failed to sync\n");
> +            ret = -EBUSY;
> +            break;
> +        }
> +
> +        if ( hypercall_preempt_check() )
> +            return hypercall_create_continuation(
> +                __HYPERVISOR_xenpmu_op, "i", XENPMU_mode_set);

Did you test this code path? I don't see how with the missing second
hypercall argument the continuation could reliably succeed.

> +    case XENPMU_mode_set:
> +    {
> +        uint32_t current_mode;
> +
> +        if ( copy_from_guest(&pmu_params, arg, 1) )
> +            return -EFAULT;
> +
> +        if ( pmu_params.val & ~(XENPMU_MODE_SELF | XENPMU_MODE_HV) )
> +            return -EINVAL;
> +
> +        /* 32-bit dom0 can only sample itself */
> +        if ( is_pv_32bit_vcpu(current) && (pmu_params.val & XENPMU_MODE_HV) )
> +            return -EINVAL;
> +
> +        /*
> +         * Return error is someone else is in the middle of changing mode ---
> +         * this is most likely indication of two system administrators
> +         * working against each other.
> +         * If continuation from vpmu_force_context_switch() is still pending
> +         * we can proceed here without getting the lock:
> +         * vpmu_force_context_switch() will check whether we are the vcpu 
> that
> +         * initialted it.
> +         */
> +        if ( (sync_vcpu == NULL) && !spin_trylock(&xenpmu_mode_lock) )
> +            return -EAGAIN;

Continuing without holding the lock when sync_vcpu != NULL is
bogus: What if sync_vcpu got cleared by the time
vpmu_force_context_switch() gets around to look at it?

> +
> +        current_mode = vpmu_mode;
> +        vpmu_mode = pmu_params.val;
> +
> +        if ( vpmu_mode == XENPMU_MODE_OFF )
> +        {
> +            /*
> +             * Make sure all (non-dom0) VCPUs have unloaded their VPMUs. This
> +             * can be achieved by having all physical processors go through
> +             * context_switch().
> +             */
> +            ret = vpmu_force_context_switch();
> +            if ( ret )
> +                vpmu_mode = current_mode;
> +        }
> +
> +        if ( spin_is_locked(&xenpmu_mode_lock) )
> +            spin_unlock(&xenpmu_mode_lock);

spin_is_locked() only tells you that _someone_ holds the lock - that
isn't necessarily the current vCPU.

> +        break;
> +    }
> +
> +    case XENPMU_mode_get:
> +        /* See whether we are in the middle of mode change */

Please make your comments conform to ./CODING_STYLE.

> +        if ( (sync_vcpu != NULL) || !spin_trylock(&xenpmu_mode_lock) )
> +            return -EAGAIN;
> +
> +        memset(&pmu_params, 0, sizeof(pmu_params));
> +        pmu_params.val = vpmu_mode;
> +        spin_unlock(&xenpmu_mode_lock);

Is it really meaningful to do this under lock? By the time the caller
gets to see it it's stale anyway.

> @@ -116,5 +109,21 @@ void vpmu_dump(struct vcpu *v);
>  extern int acquire_pmu_ownership(int pmu_ownership);
>  extern void release_pmu_ownership(int pmu_ownership);
>  
> +extern uint64_t vpmu_mode;
> +extern uint64_t vpmu_features;

Any particular reason for these to not be unsigned int?

Jan

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
http://lists.xen.org/xen-devel


 


Rackspace

Lists.xenproject.org is hosted with RackSpace, monitoring our
servers 24x7x365 and backed by RackSpace's Fanatical Support®.