|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [Xen-devel] [PATCH v10 07/11] vpci/bars: add handlers to map the BARs
>>> On 16.03.18 at 14:30, <roger.pau@xxxxxxxxxx> wrote:
> +static int map_range(unsigned long s, unsigned long e, void *data,
> + unsigned long *c)
> +{
> + const struct map_data *map = data;
> + int rc;
> +
> + for ( ; ; )
> + {
> + unsigned long size = e - s + 1;
> +
> + /*
> + * ARM TODOs:
> + * - On ARM whether the memory is prefetchable or not should be
> passed
> + * to map_mmio_regions in order to decide which memory attributes
> + * should be used.
> + *
> + * - {un}map_mmio_regions doesn't support preemption.
> + */
> +
> + rc = (map->map ? map_mmio_regions : unmap_mmio_regions)
> + (map->d, _gfn(s), size, _mfn(s));
This, btw, is one of those instances where a pair of direct calls
may end up being translated to an indirect one by the compiler.
Despite growing redundancy in source if you address that, I
think I agree with Andrew that we'd prefer to avoid the indirect
call here.
> +bool vpci_process_pending(struct vcpu *v)
> +{
> + if ( v->vpci.mem )
> + {
> + struct map_data data = {
> + .d = v->domain,
> + .map = v->vpci.map,
> + };
> + int rc = rangeset_consume_ranges(v->vpci.mem, map_range, &data);
> +
> + if ( rc == -ERESTART )
> + return true;
> +
> + spin_lock(&v->vpci.pdev->vpci->lock);
> + /* Disable memory decoding unconditionally on failure. */
> + modify_decoding(v->vpci.pdev, rc ? false : v->vpci.map,
> + rc ? false : v->vpci.rom_only);
These two ternary expressions could be simplified to "!rc && ..."
afaict.
> +static int __init apply_map(struct domain *d, struct pci_dev *pdev,
> + struct rangeset *mem)
> +{
> + struct map_data data = { .d = d, .map = true };
> + int rc;
> +
> + while ( (rc = rangeset_consume_ranges(mem, map_range, &data)) ==
> -ERESTART )
> + process_pending_softirqs();
> + rangeset_destroy(mem);
> + if ( rc )
> + return rc;
> + modify_decoding(pdev, true, false);
> +
> + return rc;
> +}
if ( !rc )
modify_decoding(pdev, true, false);
return rc;
would make this function have just a single return point without
adversely affecting readability.
> +static int modify_bars(const struct pci_dev *pdev, bool map, bool rom_only)
> +{
> + struct vpci_header *header = &pdev->vpci->header;
> + struct rangeset *mem = rangeset_new(NULL, NULL, 0);
> + struct pci_dev *tmp, *dev = NULL;
> + unsigned int i;
> + int rc;
> +
> + if ( !mem )
> + return -ENOMEM;
> +
> + /*
> + * Create a rangeset that represents the current device BARs memory
> region
> + * and compare it against all the currently active BAR memory regions. If
> + * an overlap is found, subtract it from the region to be
> mapped/unmapped.
> + *
> + * First fill the rangeset with all the BARs of this device or with the
> ROM
> + * BAR only, depending on whether the guest is toggling the memory decode
> + * bit of the command register, or the enable bit of the ROM BAR
> register.
> + */
> + for ( i = 0; i < ARRAY_SIZE(header->bars); i++ )
> + {
> + const struct vpci_bar *bar = &header->bars[i];
> +
> + if ( !MAPPABLE_BAR(bar) ||
> + (rom_only ? bar->type != VPCI_BAR_ROM
> + : (bar->type == VPCI_BAR_ROM &&
> !header->rom_enabled)) )
> + continue;
> +
> + rc = rangeset_add_range(mem, PFN_DOWN(bar->addr),
> + PFN_DOWN(bar->addr + bar->size - 1));
> + if ( rc )
> + {
> + printk(XENLOG_G_WARNING
> + "Failed to add [%" PRI_gfn ", %" PRI_gfn "]: %d\n",
> + PFN_DOWN(bar->addr), PFN_DOWN(bar->addr + bar->size - 1),
> + rc);
> + rangeset_destroy(mem);
> + return rc;
> + }
> + }
> +
> + /*
> + * Check for overlaps with other BARs. Note that only BARs that are
> + * currently mapped (enabled) are checked for overlaps.
> + */
> + list_for_each_entry(tmp, &pdev->domain->arch.pdev_list, domain_list)
> + {
> + if ( tmp == pdev )
> + {
> + /*
> + * Need to store the device so it's not constified and
> + * maybe_defer_map can modify it in case of error.
> + */
> + dev = tmp;
There's no maybe_defer_map anymore.
And then I'm having a problem with this comment on const-ness:
apply_map() only wants to hand the device to modify_decoding(),
whose respective argument is const. defer_map() stores the
pointer, but afaics again only for vpci_process_pending() to hand
it on to modify_decoding(); the lock the function takes is in a
structure pointed to from the device, so unaffected by the const.
> + if ( !rom_only )
> + /*
> + * If memory decoding is toggled avoid checking against the
> + * same device, or else all regions will be removed from the
> + * memory map in the unmap case.
> + */
> + continue;
> + }
> +
> + for ( i = 0; i < ARRAY_SIZE(tmp->vpci->header.bars); i++ )
> + {
> + const struct vpci_bar *bar = &tmp->vpci->header.bars[i];
> + unsigned long start = PFN_DOWN(bar->addr);
> + unsigned long end = PFN_DOWN(bar->addr + bar->size - 1);
> +
> + if ( !bar->enabled || !rangeset_overlaps_range(mem, start, end)
> ||
> + /*
> + * If only the ROM enable bit is toggled check against other
> + * BARs in the same device for overlaps, but not against the
> + * same ROM BAR.
> + */
> + (rom_only && tmp == pdev && bar->type == VPCI_BAR_ROM) )
> + continue;
> +
> + rc = rangeset_remove_range(mem, start, end);
> + if ( rc )
> + {
> + printk(XENLOG_G_WARNING
> + "Failed to remove [%" PRI_gfn ", %" PRI_gfn "]: %d\n",
> + start, end, rc);
> + rangeset_destroy(mem);
> + return rc;
> + }
> + }
> + }
> +
> + ASSERT(dev);
> +
> + if ( system_state < SYS_STATE_active )
> + {
> + /*
> + * Mappings might be created when building Dom0 if the memory
> decoding
> + * bit of PCI devices is enabled. In that case it's not possible to
> + * defer the operation, so call apply_map in order to create the
> + * mappings right away. Note that at build time this function will
> only
> + * be called iff the memory decoding bit is enabled, thus the
> operation
> + * will always be to establish mappings and process all the BARs.
> + */
> + ASSERT(map && !rom_only);
> + return apply_map(pdev->domain, dev, mem);
> + }
> +
> + defer_map(pdev->domain, dev, mem, map, rom_only);
In both calls, in case the dual pdev/dev needs to be retained for
some reason, please use only/consistently one of the two here,
to make visible at the first glance that the domain passed is the
owner of the device passed.
> +static void rom_write(const struct pci_dev *pdev, unsigned int reg,
> + uint32_t val, void *data)
> +{
> + struct vpci_header *header = &pdev->vpci->header;
> + struct vpci_bar *rom = data;
> + uint8_t slot = PCI_SLOT(pdev->devfn), func = PCI_FUNC(pdev->devfn);
> + uint16_t cmd = pci_conf_read16(pdev->seg, pdev->bus, slot, func,
> + PCI_COMMAND);
> + bool new_enabled = val & PCI_ROM_ADDRESS_ENABLE;
> +
> + if ( (cmd & PCI_COMMAND_MEMORY) && header->rom_enabled && new_enabled )
> + {
> + gprintk(XENLOG_WARNING,
> + "%04x:%02x:%02x.%u: ignored ROM BAR write with memory
> decoding enabled\n",
> + pdev->seg, pdev->bus, slot, func);
> + return;
> + }
> +
> + if ( !header->rom_enabled )
> + rom->addr = val & PCI_ROM_ADDRESS_MASK;
> +
> + /* Check if ROM BAR should be mapped/unmapped. */
> + if ( (cmd & PCI_COMMAND_MEMORY) && header->rom_enabled != new_enabled )
> + {
> + if ( modify_bars(pdev, new_enabled, true) )
> + /*
> + * Return on error in order to avoid updating the 'addr' field.
> No
Note that if you inverted the outer if() condition, you would get
away with a level less of indentation of this (now inner) if() body.
> + * memory has been added or removed from the p2m (because the
> + * actual p2m changes are deferred in maybe_defer_map) and the
> ROM
> + * enable bit has not been changed, so leave everything as-is,
> + * hoping the guest will realize and try again.
> + */
> + return;
> + }
> + else
> + {
> + header->rom_enabled = new_enabled;
> + pci_conf_write32(pdev->seg, pdev->bus, slot, func, reg, val);
> + }
> +
> + if ( !new_enabled )
> + rom->addr = val & PCI_ROM_ADDRESS_MASK;
I'm struggling to understand this update, not the least seeing the
other update further up.
Jan
_______________________________________________
Xen-devel mailing list
Xen-devel@xxxxxxxxxxxxxxxxxxxx
https://lists.xenproject.org/mailman/listinfo/xen-devel
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |