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

Re: [Xen-devel] [PATCH 4/4] x86/hyperv: L0 assisted TLB flush



On Thu, Feb 13, 2020 at 12:20:33PM +0000, Wei Liu wrote:
> On Wed, Feb 12, 2020 at 06:43:47PM +0100, Roger Pau Monné wrote:
> > On Wed, Feb 12, 2020 at 04:09:18PM +0000, Wei Liu wrote:
> > > +static uint64_t flush_tlb_ex(const cpumask_t *mask, const void *va,
> > > +                             unsigned int flags)
> > > +{
> > > +    struct hv_tlb_flush_ex *flush = this_cpu(hv_input_page);
> > > +    int nr_banks;
> > > +    unsigned int max_gvas;
> > > +    unsigned int order = flags & FLUSH_ORDER_MASK;
> > > +    uint64_t ret;
> > > +
> > > +    ASSERT(flush);
> > > +    ASSERT(!local_irq_is_enabled());
> > 
> > Can you turn this into an if condition with ASSERT_UNREACHABLE and
> > return ~0ULL? (as I think that signals an error).
> > 
> 
> There is no need for that. This function will always be internal to
> Hyper-V in the foreseeable future. If it is ever called with IRQ enabled
> something is wrong with the code.

But iff it ever manages to be called violating one of those conditions
things will go badly I assume?

It would be better to stay on the safe side and simply return an error
when the conditions are no meet, and assert in the debug build.

> 
> > > +
> > > +    if ( !(ms_hyperv.hints & HV_X64_EX_PROCESSOR_MASKS_RECOMMENDED) )
> > > +        return ~0ULL;
> > > +
> > > +    flush->address_space = 0;
> > > +    flush->flags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES;
> > > +    if ( !(flags & FLUSH_TLB_GLOBAL) )
> > > +        flush->flags |= HV_FLUSH_NON_GLOBAL_MAPPINGS_ONLY;
> > > +
> > > +    flush->hv_vp_set.valid_bank_mask = 0;
> > > +    flush->hv_vp_set.format = HV_GENERIC_SET_SPARSE_4K;
> > > +
> > > +    nr_banks = cpumask_to_vpset(&flush->hv_vp_set, mask);
> > > +    if ( nr_banks < 0 )
> > > +        return ~0ULL;
> > > +
> > > +    max_gvas =
> > > +        (PAGE_SIZE - sizeof(*flush) - nr_banks *
> > > +         sizeof(flush->hv_vp_set.bank_contents[0])) /
> > > +        sizeof(uint64_t);       /* gva is represented as uint64_t */
> > > +
> > > +    /*
> > > +     * Flush the entire address space if va is NULL or if there is not
> > > +     * enough space for gva_list.
> > > +     */
> > > +    if ( !va || (ORDER_TO_BYTES(order) / HV_TLB_FLUSH_UNIT) > max_gvas )
> > > +        ret = hv_do_rep_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE_EX, 
> > > 0,
> > > +                                  nr_banks, virt_to_maddr(flush), 0);
> > > +    else
> > > +    {
> > > +        uint64_t *gva_list = (uint64_t *)flush + sizeof(*flush) + 
> > > nr_banks;
> > 
> > Don't you need nr_banks * sizeof(flush->hv_vp_set.bank_contents) in
> > order to calculate the position of the gva_list?
> > 
> 
> The pointer arithmetic is done on uint64_t pointers so it already takes
> into account sizeof(bank_contents[0]).

Oh, then the sizeof(*flush) should be divided by sizeof(uint64_t)?

> > > +        unsigned int gvas = fill_gva_list(gva_list, va, order);
> > > +
> > > +        ret = hv_do_rep_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST_EX,
> > > +                                  gvas, nr_banks, virt_to_maddr(flush), 
> > > 0);
> > > +    }
> > > +
> > > +    return ret;
> > > +}
> > > +
> > >  int hyperv_flush_tlb(const cpumask_t *mask, const void *va,
> > >                       unsigned int flags)
> > >  {
> > > -    return -EOPNOTSUPP;
> > > +    unsigned long irq_flags;
> > > +    struct hv_tlb_flush *flush = this_cpu(hv_input_page);
> > > +    uint64_t ret;
> > > +    unsigned int order = flags & FLUSH_ORDER_MASK;
> > > +    unsigned int max_gvas;
> > > +
> > > +    ASSERT(flush);
> > > +    ASSERT(!cpumask_empty(mask));
> > > +
> > > +    local_irq_save(irq_flags);
> > > +
> > > +    flush->address_space = 0;
> > > +    flush->flags = HV_FLUSH_ALL_VIRTUAL_ADDRESS_SPACES;
> > > +    flush->processor_mask = 0;
> > > +    if ( !(flags & FLUSH_TLB_GLOBAL) )
> > > +        flush->flags |= HV_FLUSH_NON_GLOBAL_MAPPINGS_ONLY;
> > > +
> > > +    if ( cpumask_equal(mask, &cpu_online_map) )
> > > +        flush->flags |= HV_FLUSH_ALL_PROCESSORS;
> > > +    else
> > > +    {
> > > +        int cpu;
> > 
> > unsigned int.
> > 
> 
> I picked int here and above because all the cpumask functions return
> int. I don't mind changing it to unsigned int -- it makes no practical
> difference.

Those should likely return unsigned ints also, as I don't think
cpumask can return errors. I prefer unsigned int, since negative cpu
values make no sense.

> > > +
> > > +        /*
> > > +         * Normally VP indices are in ascending order and match Xen's
> > > +         * idea of CPU ids. Check the last index to see if VP index is
> > > +         * >= 64. If so, we can skip setting up parameters for
> > > +         * non-applicable hypercalls without looking further.
> > > +         */
> > > +        if ( hv_vp_index(cpumask_last(mask)) >= 64 )
> > > +            goto do_ex_hypercall;
> > > +
> > > +        for_each_cpu ( cpu, mask )
> > > +        {
> > > +            uint32_t vpid = hv_vp_index(cpu);
> > > +
> > > +            if ( vpid > ms_hyperv.max_vp_index )
> > > +            {
> > > +                local_irq_restore(irq_flags);
> > > +                return -ENXIO;
> > > +            }
> > > +
> > > +            if ( vpid >= 64 )
> > > +                goto do_ex_hypercall;
> > > +
> > > +            __set_bit(vpid, &flush->processor_mask);
> > > +        }
> > > +    }
> > > +
> > > +    max_gvas = (PAGE_SIZE - sizeof(*flush)) / sizeof(flush->gva_list[0]);
> > > +
> > > +    /*
> > > +     * Flush the entire address space if va is NULL or if there is not
> > > +     * enough space for gva_list.
> > > +     */
> > > +    if ( !va || (ORDER_TO_BYTES(order) / HV_TLB_FLUSH_UNIT) > max_gvas )
> > > +        ret = hv_do_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_SPACE,
> > > +                              virt_to_maddr(flush), 0);
> > > +    else
> > > +    {
> > > +        unsigned int gvas = fill_gva_list(flush->gva_list, va, order);
> > > +
> > > +        ret = hv_do_rep_hypercall(HVCALL_FLUSH_VIRTUAL_ADDRESS_LIST, 
> > > gvas, 0,
> > > +                                  virt_to_maddr(flush), 0);
> > > +    }
> > > +
> > > +    goto done;
> > > +
> > > + do_ex_hypercall:
> > > +    ret = flush_tlb_ex(mask, va, flags);
> > > +
> > > + done:
> > > +    local_irq_restore(irq_flags);
> > > +
> > > +    return ret & HV_HYPERCALL_RESULT_MASK;
> > 
> > Will this return an error code that uses the same space as Xen's errno
> > values?
> > 
> 
> No, it won't. It returns Hyper-V's status code (0 still means success).
> 
> I didn't think that was a big deal because non-zero values meant errors.
> And the upper layer didn't care about the exact error values (yet).

Hm, I would rather have this return an error value in the errno.h
range. ie:

return ret & HV_HYPERCALL_RESULT_MASK ? -EINVAL : 0;

Or something along this lines, but long term you will need some kind
of mapping between HyperV and Xen error codes IMO.

Thanks, Roger.

_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel

 


Rackspace

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