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

Re: [Xen-devel] [PATCH v2 03/17] x86/hvm: unify internal portio and mmio intercepts



>>> On 11.06.15 at 17:42, <paul.durrant@xxxxxxxxxx> wrote:
> - fail2:
> + fail6:
>      rtc_deinit(d);
>      stdvga_deinit(d);
>      vioapic_deinit(d);
> - fail1:
> + fail5:
>      if ( is_hardware_domain(d) )
>          xfree(d->arch.hvm_domain.io_bitmap);
> -    xfree(d->arch.hvm_domain.io_handler);
> + fail4:
> +    xfree(d->arch.hvm_domain.portio_handler);
> + fail3:
> +    xfree(d->arch.hvm_domain.mmio_handler);
> + fail2:
>      xfree(d->arch.hvm_domain.params);
> + fail1:
>   fail0:

We certainly don't want two consecutive labels. And with struct
domain starting out zero-initialized you don't need these many
earlier labels either (xfree(NULL) is perfectly fine).

> @@ -86,31 +175,40 @@ static int hvm_mmio_access(struct vcpu *v,
>              }
>              else
>              {
> -                rc = read(v, p->addr + step * i, p->size, &data);
> +                addr = (p->type == IOREQ_TYPE_COPY) ?
> +                    p->addr + step * i :
> +                    p->addr;
> +                rc = ops->read(handler, v, addr, p->size, &data);

Why this IOREQ_TYPE_COPY dependency that wasn't there before?

>                  if ( rc != X86EMUL_OKAY )
>                      break;
>              }
> -            switch ( hvm_copy_to_guest_phys(p->data + step * i,
> -                                            &data, p->size) )
> +
> +            if ( p->data_is_ptr )
>              {
> -            case HVMCOPY_okay:
> -                break;
> -            case HVMCOPY_gfn_paged_out:
> -            case HVMCOPY_gfn_shared:
> -                rc = X86EMUL_RETRY;
> -                break;
> -            case HVMCOPY_bad_gfn_to_mfn:
> -                /* Drop the write as real hardware would. */
> -                continue;
> -            case HVMCOPY_bad_gva_to_gfn:
> -                ASSERT(0);
> -                /* fall through */
> -            default:
> -                rc = X86EMUL_UNHANDLEABLE;
> -                break;
> +                switch ( hvm_copy_to_guest_phys(p->data + step * i,
> +                                                &data, p->size) )
> +                {
> +                case HVMCOPY_okay:
> +                    break;
> +                case HVMCOPY_gfn_paged_out:
> +                case HVMCOPY_gfn_shared:
> +                    rc = X86EMUL_RETRY;
> +                    break;
> +                case HVMCOPY_bad_gfn_to_mfn:
> +                    /* Drop the write as real hardware would. */
> +                    continue;
> +                case HVMCOPY_bad_gva_to_gfn:
> +                    ASSERT(0);

ASSERT_UNREACHABLE() please if you already touch such code.

> @@ -163,240 +270,182 @@ static int hvm_mmio_access(struct vcpu *v,
>      return rc;
>  }
>  
> -bool_t hvm_mmio_internal(paddr_t gpa)
> -{
> -    struct vcpu *curr = current;
> -    unsigned int i;
> -
> -    for ( i = 0; i < HVM_MMIO_HANDLER_NR; ++i )
> -        if ( hvm_mmio_handlers[i]->check(curr, gpa) )
> -            return 1;
> +static DEFINE_RCU_READ_LOCK(intercept_rcu_lock);
>  
> -    return 0;
> -}
> -
> -int hvm_mmio_intercept(ioreq_t *p)
> +struct hvm_io_handler *hvm_find_io_handler(struct vcpu *v,

I suppose v == current here, i.e. it should be named curr. And I
guess the function should return a pointer to const.

> +                                           ioreq_t *p)
>  {
> -    struct vcpu *v = current;
> -    int i;
> +    struct domain *d = v->domain;
> +    struct hvm_io_handler *handler;
>  
> -    for ( i = 0; i < HVM_MMIO_HANDLER_NR; i++ )
> -    {
> -        hvm_mmio_check_t check =
> -            hvm_mmio_handlers[i]->check;
> +    rcu_read_lock(&intercept_rcu_lock);
>  
> -        if ( check(v, p->addr) )
> -        {
> -            if ( unlikely(p->count > 1) &&
> -                 !check(v, unlikely(p->df)
> -                        ? p->addr - (p->count - 1L) * p->size
> -                        : p->addr + (p->count - 1L) * p->size) )
> -                p->count = 1;
> -
> -            return hvm_mmio_access(
> -                v, p,
> -                hvm_mmio_handlers[i]->read,
> -                hvm_mmio_handlers[i]->write);
> -        }
> -    }
> -
> -    return X86EMUL_UNHANDLEABLE;
> -}
> -
> -static int process_portio_intercept(portio_action_t action, ioreq_t *p)
> -{
> -    struct hvm_vcpu_io *vio = &current->arch.hvm_vcpu.hvm_io;
> -    int rc = X86EMUL_OKAY, i, step = p->df ? -p->size : p->size;
> -    uint32_t data;
> -
> -    if ( !p->data_is_ptr )
> +    list_for_each_entry( handler,
> +                         &d->arch.hvm_domain.io_handler.list,
> +                         list_entry )
>      {
> -        if ( p->dir == IOREQ_READ )
> -        {
> -            if ( vio->mmio_retrying )
> -            {
> -                if ( vio->mmio_large_read_bytes != p->size )
> -                    return X86EMUL_UNHANDLEABLE;
> -                memcpy(&data, vio->mmio_large_read, p->size);
> -                vio->mmio_large_read_bytes = 0;
> -                vio->mmio_retrying = 0;
> -            }
> -            else
> -                rc = action(IOREQ_READ, p->addr, p->size, &data);
> -            p->data = data;
> -        }
> -        else
> -        {
> -            data = p->data;
> -            rc = action(IOREQ_WRITE, p->addr, p->size, &data);
> -        }
> -        return rc;
> -    }
> +        const struct hvm_io_ops *ops = handler->ops;
> +        uint64_t start, end;
>  
> -    if ( p->dir == IOREQ_READ )
> -    {
> -        for ( i = 0; i < p->count; i++ )
> +        if ( handler->type != p->type )
> +            continue;
> +
> +        switch ( handler->type )
>          {
> -            if ( vio->mmio_retrying )
> +        case IOREQ_TYPE_PIO:
> +            start = p->addr;
> +            end = p->addr + p->size;
> +            break;
> +        case IOREQ_TYPE_COPY:
> +            if ( p->df )
>              {
> -                if ( vio->mmio_large_read_bytes != p->size )
> -                    return X86EMUL_UNHANDLEABLE;
> -                memcpy(&data, vio->mmio_large_read, p->size);
> -                vio->mmio_large_read_bytes = 0;
> -                vio->mmio_retrying = 0;
> +                start = (p->addr - (p->count - 1) * p->size);

You're re-introducing a problem here that I fixed not so long ago:
The way it's coded here the multiplication is a 32-bit unsigned one,
but we need it to be a 64-bit signed one. I.e. you need 1L here
(and elsewhere) instead of just 1.

> +                end = p->addr + p->size;
>              }
>              else
>              {
> -                rc = action(IOREQ_READ, p->addr, p->size, &data);
> -                if ( rc != X86EMUL_OKAY )
> -                    break;
> -            }
> -            switch ( hvm_copy_to_guest_phys(p->data + step * i,
> -                                            &data, p->size) )
> -            {
> -            case HVMCOPY_okay:
> -                break;
> -            case HVMCOPY_gfn_paged_out:
> -            case HVMCOPY_gfn_shared:
> -                rc = X86EMUL_RETRY;
> -                break;
> -            case HVMCOPY_bad_gfn_to_mfn:
> -                /* Drop the write as real hardware would. */
> -                continue;
> -            case HVMCOPY_bad_gva_to_gfn:
> -                ASSERT(0);
> -                /* fall through */
> -            default:
> -                rc = X86EMUL_UNHANDLEABLE;
> -                break;
> +                start = p->addr;
> +                end = p->addr + p->count * p->size;

Same here - one of the multiplicands needs to be extended to long.

> +done:

Labels indented by at least one blank please.

> +void register_mmio_handler(struct domain *d, const struct hvm_mmio_ops *ops)
> +{
> +    struct hvm_mmio_handler *mmio_handler;
> +    unsigned int i;
> +
> +    for ( i = 0; i < NR_MMIO_HANDLERS; i++ )
>      {
> -        if ( type != handler->hdl_list[i].type )
> -            continue;
> -        addr = handler->hdl_list[i].addr;
> -        size = handler->hdl_list[i].size;
> -        if ( (p->addr >= addr) &&
> -             ((p->addr + p->size) <= (addr + size)) )
> -        {
> -            if ( type == HVM_PORTIO )
> -                return process_portio_intercept(
> -                    handler->hdl_list[i].action.portio, p);
> +        mmio_handler = &d->arch.hvm_domain.mmio_handler[i];
>  
> -            if ( unlikely(p->count > 1) &&
> -                 (unlikely(p->df)
> -                  ? p->addr - (p->count - 1L) * p->size < addr
> -                  : p->addr + p->count * 1L * p->size - 1 >= addr + size) )
> -                p->count = 1;
> +        if ( mmio_handler->io_handler.ops == NULL )
> +            break;
> +    }
>  
> -            return handler->hdl_list[i].action.mmio(p);
> -        }
> +    BUG_ON(i == NR_MMIO_HANDLERS);
> +
> +    mmio_handler->io_handler.type = IOREQ_TYPE_COPY;
> +    mmio_handler->io_handler.ops = &mmio_ops;
> +
> +    hvm_register_io_handler(d, &mmio_handler->io_handler, 0);

So you're building a linked list of elements you could go through as
an array? And the ->io_handler initialization is the same for all array
elements. This doesn't look like clear simplification to me.

> +    mmio_handler->ops = ops;

And why is this being done after registration?

And is _all_ of this really usefully per-domain anyway?

> -void relocate_io_handler(
> -    struct domain *d, unsigned long old_addr, unsigned long new_addr,
> -    unsigned long size, int type)
> +bool_t hvm_mmio_internal(paddr_t gpa)
>  {
> -    struct hvm_io_handler *handler = d->arch.hvm_domain.io_handler;
> -    int i;
> -
> -    for ( i = 0; i < handler->num_slot; i++ )
> -        if ( (handler->hdl_list[i].addr == old_addr) &&
> -             (handler->hdl_list[i].size == size) &&
> -             (handler->hdl_list[i].type == type) )
> -            handler->hdl_list[i].addr = new_addr;
> +    ioreq_t p = {
> +        .type = IOREQ_TYPE_COPY,
> +        .addr = gpa
> +    };
> +
> +    return (hvm_find_io_handler(current, &p) != NULL);

Pointless parentheses.

> -typedef int (*portio_action_t)(
> -    int dir, uint32_t port, uint32_t bytes, uint32_t *val);
> -typedef int (*mmio_action_t)(ioreq_t *);
> -struct io_handler {
> -    int                 type;
> -    unsigned long       addr;
> -    unsigned long       size;
> -    union {
> -        portio_action_t portio;
> -        mmio_action_t   mmio;
> -        void           *ptr;
> -    } action;
> -};
> -
> -struct hvm_io_handler {
> -    int     num_slot;
> -    struct  io_handler hdl_list[MAX_IO_HANDLER];
> -};
> -
>  struct hvm_mmio_ops {
>      hvm_mmio_check_t check;
>      hvm_mmio_read_t  read;
>      hvm_mmio_write_t write;
>  };
>  
> -extern const struct hvm_mmio_ops hpet_mmio_ops;
> -extern const struct hvm_mmio_ops vlapic_mmio_ops;
> -extern const struct hvm_mmio_ops vioapic_mmio_ops;
> -extern const struct hvm_mmio_ops msixtbl_mmio_ops;
> -extern const struct hvm_mmio_ops iommu_mmio_ops;

As a result they should all become static in their source files.

> +#define NR_MMIO_HANDLERS 5
>  
> -#define HVM_MMIO_HANDLER_NR 5
> +struct hvm_mmio_handler {
> +    const struct hvm_mmio_ops *ops;
> +    struct hvm_io_handler     io_handler;
> +};
>  
> -int hvm_io_intercept(ioreq_t *p, int type);
> -void register_io_handler(
> -    struct domain *d, unsigned long addr, unsigned long size,
> -    void *action, int type);
> -void relocate_io_handler(
> -    struct domain *d, unsigned long old_addr, unsigned long new_addr,
> -    unsigned long size, int type);
> +typedef int (*portio_action_t)(
> +    int dir, uint32_t port, uint32_t bytes, uint32_t *val);
> +
> +#define NR_PORTIO_HANDLERS 16
> +
> +struct hvm_portio_handler {
> +    uint64_t              start, end;

Why uint64_t? (Note that this is simply the place I spotted this first
at - the problem appears to exist in all of the port I/O handling.)

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®.