[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



> -----Original Message-----
> From: Jan Beulich [mailto:JBeulich@xxxxxxxx]
> Sent: 17 June 2015 15:23
> To: Paul Durrant
> Cc: Andrew Cooper; xen-devel@xxxxxxxxxxxxxxxxxxxx; Keir (Xen.org)
> Subject: Re: [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).
> 

Ok. I'll see if I can condense some of them.

> > @@ -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?
> 

Because the unified function is used for both port and memory mapped I/O and 
it's only correct to increment in the addr value in the mmio case (i.e. when 
ioreq type is 'copy').

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

Ok. I'll change them as I see them.

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

Ok.

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

Ok. That's clearly a bit subtle though, since I missed it. Would it be more 
obvious to have something like:

start = p->addr - (uint64_t)p->size * (p->count - 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.
> 

Ok.

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

In the process loop, because it works on both mmio and portio, I use a generic 
io_hander struct that I can then use container_of() to get back at the outer 
portio or mmio struct when I need to. I agree it looks complex, but it's a 
relatively small price to remove the almost totally duplicated code if we have 
separate process loops for mmio and portio.

> > +    mmio_handler->ops = ops;
> 
> And why is this being done after registration?

Possibly it doesn't.

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

Maybe the portio handlers could go global too but it seems more flexible to 
make things per-domain.

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

Ok.

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

Ok.

> > +#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.)

Because the common process loop and other functions deal in 64-bit vals so that 
they don't have to care whether they are port or mmio address values. They are 
cast down to 32-bits before the action is invoked.

  Paul

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