|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH v3 8/9] vpci/msi: add MSI handlers
>>> On 27.04.17 at 16:35, <roger.pau@xxxxxxxxxx> wrote:
> Add handlers for the MSI control, address, data and mask fields in order to
> detect accesses to them and setup the interrupts as requested by the guest.
>
> Note that the pending register is not trapped, and the guest can freely
> read/write to it.
>
> Whether Xen is going to provide this functionality to Dom0 (MSI emulation) is
> controlled by the "msi" option in the dom0 field. When disabling this option
> Xen will hide the MSI capability structure from Dom0.
Unless there's an actual reason behind this, I'd view this as a
development only thing, which shouldn't be in a non-RFC patch.
> --- a/xen/arch/x86/hvm/vmsi.c
> +++ b/xen/arch/x86/hvm/vmsi.c
> @@ -622,3 +622,144 @@ void msix_write_completion(struct vcpu *v)
> if ( msixtbl_write(v, ctrl_address, 4, 0) != X86EMUL_OKAY )
> gdprintk(XENLOG_WARNING, "MSI-X write completion failure\n");
> }
> +
> +static unsigned int msi_vector(uint16_t data)
> +{
> + return (data & MSI_DATA_VECTOR_MASK) >> MSI_DATA_VECTOR_SHIFT;
I know code elsewhere does it this way, but I'm intending to switch
that to use MASK_EXTR(), and I'd like to ask to use that construct
right away in new code.
> +static unsigned int msi_flags(uint16_t data, uint64_t addr)
> +{
> + unsigned int rh, dm, dest_id, deliv_mode, trig_mode;
> +
> + rh = (addr >> MSI_ADDR_REDIRECTION_SHIFT) & 0x1;
> + dm = (addr >> MSI_ADDR_DESTMODE_SHIFT) & 0x1;
> + dest_id = (addr & MSI_ADDR_DEST_ID_MASK) >> MSI_ADDR_DEST_ID_SHIFT;
> + deliv_mode = (data >> MSI_DATA_DELIVERY_MODE_SHIFT) & 0x7;
> + trig_mode = (data >> MSI_DATA_TRIGGER_SHIFT) & 0x1;
I'm sure you've simply copied code from elsewhere, which I agree
should generally be fine. However, on top of what I did say above
I'd also like to request to drop such stray 0x prefixes, plus I think
the 7 wants a #define.
> + return dest_id | (rh << GFLAGS_SHIFT_RH) | (dm << GFLAGS_SHIFT_DM) |
> + (deliv_mode << GFLAGS_SHIFT_DELIV_MODE) |
> + (trig_mode << GFLAGS_SHIFT_TRG_MODE);
How come dest_id has no shift? Please let's not assume the shift
is zero now and forever.
> +void vpci_msi_mask(struct vpci_arch_msi *arch, unsigned int entry, bool mask)
> +{
> + struct pirq *pinfo;
> + struct irq_desc *desc;
> + unsigned long flags;
> + int irq;
> +
> + ASSERT(arch->pirq != -1);
Perhaps better ">= 0"?
> + pinfo = pirq_info(current->domain, arch->pirq + entry);
> + ASSERT(pinfo);
> +
> + irq = pinfo->arch.irq;
> + ASSERT(irq < nr_irqs);
> +
> + desc = irq_to_desc(irq);
> + ASSERT(desc);
It's not entirely clear to me where all the checks are which allow
the checks here to be ASSERT()s.
> +int vpci_msi_enable(struct vpci_arch_msi *arch, struct pci_dev *pdev,
> + uint64_t address, uint32_t data, unsigned int vectors)
> +{
> + struct msi_info msi_info = {
> + .seg = pdev->seg,
> + .bus = pdev->bus,
> + .devfn = pdev->devfn,
> + .entry_nr = vectors,
> + };
> + int index = -1, rc;
> + unsigned int i;
> +
> + ASSERT(arch->pirq == -1);
> +
> + /* Get a PIRQ. */
> + rc = allocate_and_map_msi_pirq(pdev->domain, &index, &arch->pirq,
> + &msi_info);
> + if ( rc )
> + {
> + dprintk(XENLOG_ERR, "%04x:%02x:%02x.%u: failed to map PIRQ: %d\n",
> + pdev->seg, pdev->bus, PCI_SLOT(pdev->devfn),
> + PCI_FUNC(pdev->devfn), rc);
> + return rc;
> + }
> +
> + for ( i = 0; i < vectors; i++ )
> + {
> + xen_domctl_bind_pt_irq_t bind = {
> + .hvm_domid = DOMID_SELF,
> + .machine_irq = arch->pirq + i,
> + .irq_type = PT_IRQ_TYPE_MSI,
> + .u.msi.gvec = msi_vector(data) + i,
> + .u.msi.gflags = msi_flags(data, address),
> + };
> +
> + pcidevs_lock();
> + rc = pt_irq_create_bind(pdev->domain, &bind);
> + if ( rc )
I don't think you need to hold the lock for the if() and its body. While
I see unmap_domain_pirq(), I don't really see why it does, so perhaps
there's some cleanup potential up front.
> --- a/xen/drivers/vpci/capabilities.c
> +++ b/xen/drivers/vpci/capabilities.c
> @@ -109,7 +109,7 @@ static int vpci_index_capabilities(struct pci_dev *pdev)
> return 0;
> }
>
> -static void vpci_mask_capability(struct pci_dev *pdev, uint8_t cap_id)
> +void xen_vpci_mask_capability(struct pci_dev *pdev, uint8_t cap_id)
What's the xen_ prefix good for?
> +static int vpci_msi_control_read(struct pci_dev *pdev, unsigned int reg,
> + union vpci_val *val, void *data)
> +{
> + struct vpci_msi *msi = data;
const
> + if ( msi->enabled )
> + val->word |= PCI_MSI_FLAGS_ENABLE;
> + if ( msi->masking )
> + val->word |= PCI_MSI_FLAGS_MASKBIT;
> + if ( msi->address64 )
> + val->word |= PCI_MSI_FLAGS_64BIT;
> +
> + /* Set multiple message capable. */
> + val->word |= ((fls(msi->max_vectors) - 1) << 1) & PCI_MSI_FLAGS_QMASK;
> +
> + /* Set current number of configured vectors. */
> + val->word |= ((fls(msi->guest_vectors) - 1) << 4) & PCI_MSI_FLAGS_QSIZE;
The 1 and 4 here clearly need #define-s or the use of MASK_INSR().
> +static int vpci_msi_control_write(struct pci_dev *pdev, unsigned int reg,
> + union vpci_val val, void *data)
> +{
> + struct vpci_msi *msi = data;
> + unsigned int vectors = 1 << ((val.word & PCI_MSI_FLAGS_QSIZE) >> 4);
> + int rc;
> +
> + if ( vectors > msi->max_vectors )
> + return -EINVAL;
> +
> + msi->guest_vectors = vectors;
> +
> + if ( !!(val.word & PCI_MSI_FLAGS_ENABLE) == msi->enabled )
> + return 0;
> +
> + if ( val.word & PCI_MSI_FLAGS_ENABLE )
> + {
> + ASSERT(!msi->enabled && !msi->vectors);
I can see the value of the right side, but the left (with the imediately
prior if())?
> + rc = vpci_msi_enable(&msi->arch, pdev, msi->address, msi->data,
> + vectors);
> + if ( rc )
> + return rc;
> +
> + /* Apply the mask bits. */
> + if ( msi->masking )
> + {
> + uint32_t mask = msi->mask;
> +
> + while ( mask )
> + {
> + unsigned int i = ffs(mask);
ffs(), just like fls(), returns 1-based values, so this looks to be off by
one.
> + vpci_msi_mask(&msi->arch, i, true);
> + __clear_bit(i, &mask);
> + }
> + }
> +
> + __msi_set_enable(pdev->seg, pdev->bus, PCI_SLOT(pdev->devfn),
> + PCI_FUNC(pdev->devfn), reg - PCI_MSI_FLAGS, 1);
Seems like you'll never come through msi_capability_init(); I can't
see how it can be a good idea to bypass lots of stuff.
> +static int vpci_msi_address_upper_write(struct pci_dev *pdev, unsigned int
> reg,
> + union vpci_val val, void *data)
> +{
> + struct vpci_msi *msi = data;
> +
> + /* Clear high part. */
> + msi->address &= ~GENMASK(63, 32);
> + msi->address |= (uint64_t)val.double_word << 32;
Is the value written here actually being used for anything other than
for reading back (also applicable to the high bits of the low half of the
address)?
> +static int vpci_msi_mask_read(struct pci_dev *pdev, unsigned int reg,
> + union vpci_val *val, void *data)
> +{
> + struct vpci_msi *msi = data;
> +
> + val->double_word = msi->mask;
> +
> + return 0;
> +}
> +
> +static int vpci_msi_mask_write(struct pci_dev *pdev, unsigned int reg,
> + union vpci_val val, void *data)
> +{
> + struct vpci_msi *msi = data;
> + uint32_t dmask;
> +
> + dmask = msi->mask ^ val.double_word;
> +
> + if ( !dmask )
> + return 0;
> +
> + while ( dmask && msi->enabled )
> + {
> + unsigned int i = ffs(dmask);
> +
> + vpci_msi_mask(&msi->arch, i, !test_bit(i, &msi->mask));
> + __clear_bit(i, &dmask);
> + }
I think this loop should be limited to the number of enabled vectors
(and the same likely applies then to vpci_msi_control_write()).
> +static int vpci_init_msi(struct pci_dev *pdev)
> +{
> + uint8_t seg = pdev->seg, bus = pdev->bus;
> + uint8_t slot = PCI_SLOT(pdev->devfn), func = PCI_FUNC(pdev->devfn);
> + struct vpci_msi *msi = NULL;
Pointless initializer.
> + unsigned int msi_offset;
> + uint16_t control;
> + int rc;
> +
> + msi_offset = pci_find_cap_offset(seg, bus, slot, func, PCI_CAP_ID_MSI);
> + if ( !msi_offset )
> + return 0;
> +
> + if ( !vpci_msi_enabled(pdev->domain) )
> + {
> + xen_vpci_mask_capability(pdev, PCI_CAP_ID_MSI);
> + return 0;
> + }
> +
> + msi = xzalloc(struct vpci_msi);
> + if ( !msi )
> + return -ENOMEM;
> +
> + control = pci_conf_read16(seg, bus, slot, func,
> + msi_control_reg(msi_offset));
> +
> + rc = xen_vpci_add_register(pdev, vpci_msi_control_read,
> + vpci_msi_control_write,
> + msi_control_reg(msi_offset), 2, msi);
> + if ( rc )
> + {
> + dprintk(XENLOG_ERR,
> + "%04x:%02x:%02x.%u: failed to add handler for MSI control:
> %d\n",
> + seg, bus, slot, func, rc);
> + goto error;
> + }
> +
> + /* Get the maximum number of vectors the device supports. */
> + msi->max_vectors = multi_msi_capable(control);
> + ASSERT(msi->max_vectors <= 32);
> +
> + /* Initial value after reset. */
> + msi->guest_vectors = 1;
> +
> + /* No PIRQ bind yet. */
> + vpci_msi_arch_init(&msi->arch);
> +
> + if ( is_64bit_address(control) )
> + msi->address64 = true;
> + if ( is_mask_bit_support(control) )
> + msi->masking = true;
> +
> + rc = xen_vpci_add_register(pdev, vpci_msi_address_read,
> + vpci_msi_address_write,
> + msi_lower_address_reg(msi_offset), 4, msi);
> + if ( rc )
> + {
> + dprintk(XENLOG_ERR,
> + "%04x:%02x:%02x.%u: failed to add handler for MSI address:
> %d\n",
> + seg, bus, slot, func, rc);
> + goto error;
> + }
> +
> + rc = xen_vpci_add_register(pdev, vpci_msi_data_read, vpci_msi_data_write,
> + msi_data_reg(msi_offset, msi->address64), 2,
> + msi);
> + if ( rc )
> + {
> + dprintk(XENLOG_ERR,
> + "%04x:%02x:%02x.%u: failed to add handler for MSI address:
> %d\n",
> + seg, bus, slot, func, rc);
Twice the same message text is unhelpful (and actually there's a third
one below). But iirc I did indicate anyway that I think most of them
should go away. Note also how much thy contribute to the function's
size.
> +static int __init vpci_msi_setup_keyhandler(void)
> +{
> + register_keyhandler('Z', vpci_dump_msi, "dump guest MSI state", 1);
Please let's avoid using new (and even non-intuitive) keys if at all
possible. This is Dom0 only, so can easily be chained onto e.g. the
'M' handler.
> --- a/xen/include/xen/vpci.h
> +++ b/xen/include/xen/vpci.h
> @@ -89,9 +89,35 @@ struct vpci {
>
> /* List of capabilities supported by the device. */
> struct list_head cap_list;
> +
> + /* MSI data. */
> + struct vpci_msi {
> + /* Maximum number of vectors supported by the device. */
> + unsigned int max_vectors;
> + /* Current guest-written number of vectors. */
> + unsigned int guest_vectors;
> + /* Number of vectors configured. */
> + unsigned int vectors;
So coming here I still don't really see what the difference between
these last two fields is (and hence why you need two).
Jan
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxx
https://lists.xen.org/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |