From: "Roger Pau Monné" <roger.pau@citrix.com>
To: Volodymyr Babchuk <Volodymyr_Babchuk@epam.com>
Cc: "xen-devel@lists.xenproject.org" <xen-devel@lists.xenproject.org>,
Stewart Hildebrand <stewart.hildebrand@amd.com>,
Oleksandr Andrushchenko <Oleksandr_Andrushchenko@epam.com>
Subject: Re: [PATCH v9 08/16] vpci/header: handle p2m range sets per BAR
Date: Wed, 20 Sep 2023 13:35:06 +0200 [thread overview]
Message-ID: <ZQrY6q4WXkAq1cAm@MacBookPdeRoger> (raw)
In-Reply-To: <20230829231912.4091958-9-volodymyr_babchuk@epam.com>
On Tue, Aug 29, 2023 at 11:19:44PM +0000, Volodymyr Babchuk wrote:
> From: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
>
> Instead of handling a single range set, that contains all the memory
> regions of all the BARs and ROM, have them per BAR.
> As the range sets are now created when a PCI device is added and destroyed
> when it is removed so make them named and accounted.
>
> Note that rangesets were chosen here despite there being only up to
> 3 separate ranges in each set (typically just 1). But rangeset per BAR
> was chosen for the ease of implementation and existing code re-usability.
>
> This is in preparation of making non-identity mappings in p2m for the MMIOs.
>
> Signed-off-by: Oleksandr Andrushchenko <oleksandr_andrushchenko@epam.com>
>
> ---
> Since v9:
> - removed d->vpci.map_pending in favor of checking v->vpci.pdev !=
> NULL
> - printk -> gprintk
> - renamed bar variable to fix shadowing
> - fixed bug with iterating on remote device's BARs
> - relaxed lock in vpci_process_pending
> - removed stale comment
> Since v6:
> - update according to the new locking scheme
> - remove odd fail label in modify_bars
> Since v5:
> - fix comments
> - move rangeset allocation to init_bars and only allocate
> for MAPPABLE BARs
> - check for overlap with the already setup BAR ranges
> Since v4:
> - use named range sets for BARs (Jan)
> - changes required by the new locking scheme
> - updated commit message (Jan)
> Since v3:
> - re-work vpci_cancel_pending accordingly to the per-BAR handling
> - s/num_mem_ranges/map_pending and s/uint8_t/bool
> - ASSERT(bar->mem) in modify_bars
> - create and destroy the rangesets on add/remove
> ---
> xen/drivers/vpci/header.c | 252 ++++++++++++++++++++++++++------------
> xen/drivers/vpci/vpci.c | 6 +
> xen/include/xen/vpci.h | 2 +-
> 3 files changed, 180 insertions(+), 80 deletions(-)
>
> diff --git a/xen/drivers/vpci/header.c b/xen/drivers/vpci/header.c
> index e96d7b2b37..3cc6a96849 100644
> --- a/xen/drivers/vpci/header.c
> +++ b/xen/drivers/vpci/header.c
> @@ -161,63 +161,101 @@ static void modify_decoding(const struct pci_dev *pdev, uint16_t cmd,
>
> bool vpci_process_pending(struct vcpu *v)
> {
> - if ( v->vpci.mem )
> + struct pci_dev *pdev = v->vpci.pdev;
> + struct map_data data = {
> + .d = v->domain,
> + .map = v->vpci.cmd & PCI_COMMAND_MEMORY,
> + };
> + struct vpci_header *header = NULL;
> + unsigned int i;
> +
> + if ( !pdev )
> + return false;
> +
> + read_lock(&v->domain->pci_lock);
> + header = &pdev->vpci->header;
You should likely check that pdev->vpci != NULL before accessing it,
and that the device is still assigned to the domain, v->domain ==
pdev->domain.
> + for ( i = 0; i < ARRAY_SIZE(header->bars); i++ )
> {
> - struct map_data data = {
> - .d = v->domain,
> - .map = v->vpci.cmd & PCI_COMMAND_MEMORY,
> - };
> - int rc = rangeset_consume_ranges(v->vpci.mem, map_range, &data);
> + struct vpci_bar *bar = &header->bars[i];
> + int rc;
> +
> + if ( rangeset_is_empty(bar->mem) )
> + continue;
> +
> + rc = rangeset_consume_ranges(bar->mem, map_range, &data);
>
> if ( rc == -ERESTART )
> + {
> + read_unlock(&v->domain->pci_lock);
> return true;
> + }
>
> - write_lock(&v->domain->pci_lock);
> - spin_lock(&v->vpci.pdev->vpci->lock);
> - /* Disable memory decoding unconditionally on failure. */
> - modify_decoding(v->vpci.pdev,
> - rc ? v->vpci.cmd & ~PCI_COMMAND_MEMORY : v->vpci.cmd,
> - !rc && v->vpci.rom_only);
> - spin_unlock(&v->vpci.pdev->vpci->lock);
> -
> - rangeset_destroy(v->vpci.mem);
> - v->vpci.mem = NULL;
> if ( rc )
> - /*
> - * FIXME: in case of failure remove the device from the domain.
> - * Note that there might still be leftover mappings. While this is
> - * safe for Dom0, for DomUs the domain will likely need to be
> - * killed in order to avoid leaking stale p2m mappings on
> - * failure.
> - */
> - vpci_deassign_device(v->vpci.pdev);
> - write_unlock(&v->domain->pci_lock);
> + {
> + spin_lock(&pdev->vpci->lock);
> + /* Disable memory decoding unconditionally on failure. */
> + modify_decoding(pdev, v->vpci.cmd & ~PCI_COMMAND_MEMORY,
> + false);
> + spin_unlock(&pdev->vpci->lock);
> +
> + v->vpci.pdev = NULL;
> +
> + read_unlock(&v->domain->pci_lock);
> +
> + if ( is_hardware_domain(v->domain) )
> + {
> + write_lock(&v->domain->pci_lock);
This unlock/lock dance is racy, and I'm not sure there's much point in
removing the vPCI handlers for the device, it's not likely to be
helpful to dom0. It might be better to just unconditionally disable
memory decoding and empty all the rangesets. Not sure whether there's
more cached state that would need dealing with in pdev->vpci.
Maybe as a bodge you could leave the current vpci_deassign_device()
call and check that pdev->domain == v->domain after having taken the
pci_lock.
> + vpci_deassign_device(v->vpci.pdev);
> + write_unlock(&v->domain->pci_lock);
> + }
> + else
> + {
> + domain_crash(v->domain);
> + }
> + return false;
> + }
> }
> + read_unlock(&v->domain->pci_lock);
> +
> + v->vpci.pdev = NULL;
> +
> + spin_lock(&pdev->vpci->lock);
> + modify_decoding(pdev, v->vpci.cmd, v->vpci.rom_only);
> + spin_unlock(&pdev->vpci->lock);
Why do you drop the pci_lock before calling modify_decoding()? It
needs to stay locked until operations on pdev have finished, iow:
after modify_decoding(), or else accessing the contents of pdev->vpci
is not safe, and the device could be deassigned in the meantime.
>
> return false;
> }
>
> static int __init apply_map(struct domain *d, const struct pci_dev *pdev,
> - struct rangeset *mem, uint16_t cmd)
> + uint16_t cmd)
> {
> struct map_data data = { .d = d, .map = true };
> - int rc;
> + struct vpci_header *header = &pdev->vpci->header;
> + int rc = 0;
> + unsigned int i;
>
> ASSERT(rw_is_locked(&d->pci_lock));
>
> - while ( (rc = rangeset_consume_ranges(mem, map_range, &data)) == -ERESTART )
> + for ( i = 0; i < ARRAY_SIZE(header->bars); i++ )
> {
> - /*
> - * It's safe to drop and reacquire the lock in this context
> - * without risking pdev disappearing because devices cannot be
> - * removed until the initial domain has been started.
> - */
> - read_unlock(&d->pci_lock);
> - process_pending_softirqs();
> - read_lock(&d->pci_lock);
> - }
> + struct vpci_bar *bar = &header->bars[i];
>
> - rangeset_destroy(mem);
> + if ( rangeset_is_empty(bar->mem) )
> + continue;
> +
> + while ( (rc = rangeset_consume_ranges(bar->mem, map_range,
> + &data)) == -ERESTART )
> + {
> + /*
> + * It's safe to drop and reacquire the lock in this context
> + * without risking pdev disappearing because devices cannot be
> + * removed until the initial domain has been started.
> + */
> + write_unlock(&d->pci_lock);
> + process_pending_softirqs();
> + write_lock(&d->pci_lock);
> + }
> + }
> if ( !rc )
> modify_decoding(pdev, cmd, false);
>
> @@ -225,10 +263,12 @@ static int __init apply_map(struct domain *d, const struct pci_dev *pdev,
> }
>
> static void defer_map(struct domain *d, struct pci_dev *pdev,
> - struct rangeset *mem, uint16_t cmd, bool rom_only)
> + uint16_t cmd, bool rom_only)
> {
> struct vcpu *curr = current;
>
> + ASSERT(rw_is_write_locked(&pdev->domain->pci_lock));
> +
> /*
> * FIXME: when deferring the {un}map the state of the device should not
> * be trusted. For example the enable bit is toggled after the device
> @@ -236,7 +276,6 @@ static void defer_map(struct domain *d, struct pci_dev *pdev,
> * started for the same device if the domain is not well-behaved.
> */
> curr->vpci.pdev = pdev;
> - curr->vpci.mem = mem;
> curr->vpci.cmd = cmd;
> curr->vpci.rom_only = rom_only;
> /*
> @@ -250,33 +289,33 @@ static void defer_map(struct domain *d, struct pci_dev *pdev,
> static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
> {
> struct vpci_header *header = &pdev->vpci->header;
> - struct rangeset *mem = rangeset_new(NULL, NULL, 0);
> struct pci_dev *tmp, *dev = NULL;
> const struct domain *d;
> const struct vpci_msix *msix = pdev->vpci->msix;
> - unsigned int i;
> + unsigned int i, j;
> int rc;
>
> ASSERT(rw_is_write_locked(&pdev->domain->pci_lock));
>
> - 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.
> + * Create a rangeset per BAR that represents the current device 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
> + * First fill the rangesets with the BAR 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];
> + struct vpci_bar *bar = &header->bars[i];
> unsigned long start = PFN_DOWN(bar->addr);
> unsigned long end = PFN_DOWN(bar->addr + bar->size - 1);
>
> + if ( !bar->mem )
> + continue;
> +
> if ( !MAPPABLE_BAR(bar) ||
> (rom_only ? bar->type != VPCI_BAR_ROM
> : (bar->type == VPCI_BAR_ROM && !header->rom_enabled)) ||
> @@ -292,14 +331,31 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
> continue;
> }
>
> - rc = rangeset_add_range(mem, start, end);
> + rc = rangeset_add_range(bar->mem, start, end);
> if ( rc )
> {
> printk(XENLOG_G_WARNING "Failed to add [%lx, %lx]: %d\n",
> start, end, rc);
> - rangeset_destroy(mem);
> return rc;
> }
> +
> + /* Check for overlap with the already setup BAR ranges. */
> + for ( j = 0; j < i; j++ )
> + {
> + struct vpci_bar *prev_bar = &header->bars[j];
> +
> + if ( rangeset_is_empty(prev_bar->mem) )
> + continue;
> +
> + rc = rangeset_remove_range(prev_bar->mem, start, end);
> + if ( rc )
> + {
> + gprintk(XENLOG_WARNING,
> + "%pp: failed to remove overlapping range [%lx, %lx]: %d\n",
> + &pdev->sbdf, start, end, rc);
> + return rc;
> + }
> + }
> }
>
> /* Remove any MSIX regions if present. */
> @@ -309,14 +365,21 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
> unsigned long end = PFN_DOWN(vmsix_table_addr(pdev->vpci, i) +
> vmsix_table_size(pdev->vpci, i) - 1);
>
> - rc = rangeset_remove_range(mem, start, end);
> - if ( rc )
> + for ( j = 0; j < ARRAY_SIZE(header->bars); j++ )
> {
> - printk(XENLOG_G_WARNING
> - "Failed to remove MSIX table [%lx, %lx]: %d\n",
> - start, end, rc);
> - rangeset_destroy(mem);
> - return rc;
> + const struct vpci_bar *bar = &header->bars[j];
> +
> + if ( rangeset_is_empty(bar->mem) )
> + continue;
> +
> + rc = rangeset_remove_range(bar->mem, start, end);
> + if ( rc )
> + {
> + gprintk(XENLOG_WARNING,
> + "%pp: failed to remove MSIX table [%lx, %lx]: %d\n",
> + &pdev->sbdf, start, end, rc);
> + return rc;
> + }
> }
> }
>
> @@ -356,27 +419,34 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
>
> 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) )
> + const struct vpci_bar *remote_bar = &tmp->vpci->header.bars[i];
> + unsigned long start = PFN_DOWN(remote_bar->addr);
> + unsigned long end = PFN_DOWN(remote_bar->addr +
> + remote_bar->size - 1);
> +
> + if ( !remote_bar->enabled )
> continue;
>
> - rc = rangeset_remove_range(mem, start, end);
> - if ( rc )
> + for ( j = 0; j < ARRAY_SIZE(header->bars); j++)
> {
> - printk(XENLOG_G_WARNING "Failed to remove [%lx, %lx]: %d\n",
> - start, end, rc);
> - rangeset_destroy(mem);
> - return rc;
> + const struct vpci_bar *bar = &header->bars[j];
> + if ( !rangeset_overlaps_range(bar->mem, start, end) ||
Missing newline between local variable definition and code.
> + /*
> + * 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(bar->mem, start, end);
> + if ( rc )
> + {
> + gprintk(XENLOG_WARNING,
> + "%pp: failed to remove [%lx, %lx]: %d\n",
> + &pdev->sbdf, start, end, rc);
> + return rc;
> + }
> }
> }
> }
> @@ -400,10 +470,10 @@ static int modify_bars(const struct pci_dev *pdev, uint16_t cmd, bool rom_only)
> * will always be to establish mappings and process all the BARs.
> */
> ASSERT((cmd & PCI_COMMAND_MEMORY) && !rom_only);
> - return apply_map(pdev->domain, pdev, mem, cmd);
> + return apply_map(pdev->domain, pdev, cmd);
> }
>
> - defer_map(dev->domain, dev, mem, cmd, rom_only);
> + defer_map(dev->domain, dev, cmd, rom_only);
>
> return 0;
> }
> @@ -595,6 +665,20 @@ static void cf_check rom_write(
> rom->addr = val & PCI_ROM_ADDRESS_MASK;
> }
>
> +static int bar_add_rangeset(const struct pci_dev *pdev, struct vpci_bar *bar,
> + unsigned int i)
> +{
> + char str[32];
> +
> + snprintf(str, sizeof(str), "%pp:BAR%d", &pdev->sbdf, i);
%u for i.
> +
> + bar->mem = rangeset_new(pdev->domain, str, RANGESETF_no_print);
> + if ( !bar->mem )
> + return -ENOMEM;
> +
> + return 0;
Could be simplified as:
return !bar->mem ? -ENOMEM : 0;
But I don't have a strong opinion, I understand some people might find
this obscure.
> +}
> +
> static int cf_check init_bars(struct pci_dev *pdev)
> {
> uint16_t cmd;
> @@ -675,6 +759,10 @@ static int cf_check init_bars(struct pci_dev *pdev)
> else
> bars[i].type = VPCI_BAR_MEM32;
>
> + rc = bar_add_rangeset(pdev, &bars[i], i);
> + if ( rc )
> + return rc;
Don't you need to use the fail label in order to restore the previous
command register value on the device? (here and below)
Thanks, Roger.
next prev parent reply other threads:[~2023-09-20 11:35 UTC|newest]
Thread overview: 60+ messages / expand[flat|nested] mbox.gz Atom feed top
2023-08-29 23:19 [PATCH v9 00/16] PCI devices passthrough on Arm, part 3 Volodymyr Babchuk
2023-08-29 23:19 ` [PATCH v9 02/16] vpci: use per-domain PCI lock to protect vpci structure Volodymyr Babchuk
2023-09-19 15:39 ` Roger Pau Monné
2023-09-19 15:55 ` Jan Beulich
2023-09-20 8:12 ` Roger Pau Monné
2023-09-19 16:20 ` Stewart Hildebrand
2023-09-20 8:09 ` Roger Pau Monné
2023-09-20 13:56 ` Stewart Hildebrand
2023-09-21 7:42 ` Jan Beulich
2023-09-21 9:00 ` Roger Pau Monné
2023-09-20 19:16 ` Stewart Hildebrand
2023-09-21 9:41 ` Roger Pau Monné
2023-09-25 23:03 ` Volodymyr Babchuk
2023-08-29 23:19 ` [PATCH v9 03/16] vpci: restrict unhandled read/write operations for guests Volodymyr Babchuk
2023-08-29 23:19 ` [PATCH v9 01/16] pci: introduce per-domain PCI rwlock Volodymyr Babchuk
2023-09-19 14:09 ` Roger Pau Monné
2023-09-25 22:44 ` Volodymyr Babchuk
2023-08-29 23:19 ` [PATCH v9 06/16] vpci/header: implement guest BAR register handlers Volodymyr Babchuk
2023-09-01 5:25 ` Stewart Hildebrand
2023-09-20 9:49 ` Roger Pau Monné
2023-09-20 14:18 ` Stewart Hildebrand
2023-08-29 23:19 ` [PATCH v9 04/16] vpci: add hooks for PCI device assign/de-assign Volodymyr Babchuk
2023-09-12 9:37 ` Jan Beulich
2023-09-12 23:41 ` Volodymyr Babchuk
2023-09-13 5:58 ` Jan Beulich
2023-09-13 23:53 ` Volodymyr Babchuk
2023-09-20 8:41 ` Roger Pau Monné
2023-09-20 8:39 ` Roger Pau Monné
2023-08-29 23:19 ` [PATCH v9 07/16] rangeset: add RANGESETF_no_print flag Volodymyr Babchuk
2023-08-29 23:19 ` [PATCH v9 05/16] vpci/header: rework exit path in init_bars Volodymyr Babchuk
2023-09-20 8:49 ` Roger Pau Monné
2023-08-29 23:19 ` [PATCH v9 09/16] vpci/header: program p2m with guest BAR view Volodymyr Babchuk
2023-09-21 10:34 ` Roger Pau Monné
2023-08-29 23:19 ` [PATCH v9 08/16] vpci/header: handle p2m range sets per BAR Volodymyr Babchuk
2023-09-20 11:35 ` Roger Pau Monné [this message]
2023-09-27 18:18 ` Stewart Hildebrand
2023-08-29 23:19 ` [PATCH v9 10/16] vpci/header: emulate PCI_COMMAND register for guests Volodymyr Babchuk
2023-09-01 5:23 ` Stewart Hildebrand
2023-09-21 13:18 ` Roger Pau Monné
2023-08-29 23:19 ` [PATCH v9 11/16] vpci/header: reset the command register when adding devices Volodymyr Babchuk
2023-09-21 13:30 ` Roger Pau Monné
2023-08-29 23:19 ` [PATCH v9 13/16] xen/arm: translate virtual PCI bus topology for guests Volodymyr Babchuk
2023-09-22 8:32 ` Roger Pau Monné
2023-08-29 23:19 ` [PATCH v9 14/16] xen/arm: account IO handlers for emulated PCI MSI-X Volodymyr Babchuk
2023-08-29 23:19 ` [PATCH v9 12/16] vpci: add initial support for virtual PCI bus topology Volodymyr Babchuk
2023-08-30 7:37 ` Jan Beulich
2023-08-31 21:12 ` Volodymyr Babchuk
2023-09-21 16:03 ` Roger Pau Monné
2023-08-29 23:19 ` [PATCH v9 16/16] xen/arm: vpci: permit access to guest vpci space Volodymyr Babchuk
2023-09-26 0:12 ` Stewart Hildebrand
2023-08-29 23:19 ` [PATCH v9 15/16] xen/arm: vpci: check guest range Volodymyr Babchuk
2023-09-22 8:44 ` Roger Pau Monné
2023-09-25 21:49 ` Stewart Hildebrand
2023-09-26 8:07 ` Roger Pau Monné
2023-09-26 15:27 ` Stewart Hildebrand
2023-09-26 15:48 ` Roger Pau Monné
2023-09-27 18:03 ` Stewart Hildebrand
2023-09-28 8:28 ` Roger Pau Monné
2023-09-28 18:28 ` Stewart Hildebrand
2023-10-02 11:49 ` Roger Pau Monné
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=ZQrY6q4WXkAq1cAm@MacBookPdeRoger \
--to=roger.pau@citrix.com \
--cc=Oleksandr_Andrushchenko@epam.com \
--cc=Volodymyr_Babchuk@epam.com \
--cc=stewart.hildebrand@amd.com \
--cc=xen-devel@lists.xenproject.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.