|
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index] Re: [PATCH v3] vpci: Add resizable bar support
On 2024/12/16 18:24, Roger Pau Monné wrote:
> On Fri, Dec 13, 2024 at 01:42:32PM +0800, Jiqian Chen wrote:
>> Some devices, like discrete GPU of amd, support resizable bar
>> capability, but vpci of Xen doesn't support this feature, so
>> they fail to resize bars and then cause probing failure.
>>
>> According to PCIe spec, each bar that supports resizing has
>> two registers, PCI_REBAR_CAP and PCI_REBAR_CTRL. So, add
>> handlers for them to support resizing the size of BARs.
>>
>> Signed-off-by: Jiqian Chen <Jiqian.Chen@xxxxxxx>
>> ---
>> Hi all,
>> v2->v3 changes:
>> * Used "bar->enabled" to replace "pci_conf_read16(pdev->sbdf, PCI_COMMAND) &
>> PCI_COMMAND_MEMORY",
>> and added comments why it needs this check.
>> * Added "!is_hardware_domain(pdev->domain)" check in init_rebar() to return
>> EOPNOTSUPP for domUs.
>> * Moved BAR type and index check into init_rebar(), then only need to check
>> once.
>> * Added 'U' suffix for macro PCI_REBAR_CAP_SIZES.
>> * Added macro PCI_REBAR_SIZE_BIAS to represent 20.
>>
>> TODO: need to hide ReBar capability from hardware domain when init_rebar()
>> fails.
>>
>> Best regards,
>> Jiqian Chen.
>>
>> v1->v2 changes:
>> * In rebar_ctrl_write, to check if memory decoding is enabled, and added
>> some checks for the type of Bar.
>> * Added vpci_hw_write32 to handle PCI_REBAR_CAP's write, since there is
>> no write limitation of dom0.
>> * And has many other minor modifications as well.
>> ---
>> xen/drivers/vpci/Makefile | 2 +-
>> xen/drivers/vpci/rebar.c | 130 +++++++++++++++++++++++++++++++++++++
>> xen/drivers/vpci/vpci.c | 6 ++
>> xen/include/xen/pci_regs.h | 13 ++++
>> xen/include/xen/vpci.h | 2 +
>> 5 files changed, 152 insertions(+), 1 deletion(-)
>> create mode 100644 xen/drivers/vpci/rebar.c
>>
>> diff --git a/xen/drivers/vpci/Makefile b/xen/drivers/vpci/Makefile
>> index 1a1413b93e76..a7c8a30a8956 100644
>> --- a/xen/drivers/vpci/Makefile
>> +++ b/xen/drivers/vpci/Makefile
>> @@ -1,2 +1,2 @@
>> -obj-y += vpci.o header.o
>> +obj-y += vpci.o header.o rebar.o
>> obj-$(CONFIG_HAS_PCI_MSI) += msi.o msix.o
>> diff --git a/xen/drivers/vpci/rebar.c b/xen/drivers/vpci/rebar.c
>> new file mode 100644
>> index 000000000000..39432c3271a4
>> --- /dev/null
>> +++ b/xen/drivers/vpci/rebar.c
>> @@ -0,0 +1,130 @@
>> +/* SPDX-License-Identifier: GPL-2.0-only */
>> +/*
>> + * Copyright (C) 2024 Advanced Micro Devices, Inc. All Rights Reserved.
>> + *
>> + * Author: Jiqian Chen <Jiqian.Chen@xxxxxxx>
>> + */
>> +
>> +#include <xen/hypercall.h>
>
> Do you really need the hypercall header?
I will change to <xen/sched.h> since is_hardware_domain() needs.
>
>> +#include <xen/vpci.h>
>> +
>> +static void cf_check rebar_ctrl_write(const struct pci_dev *pdev,
>> + unsigned int reg,
>> + uint32_t val,
>> + void *data)
>> +{
>> + uint64_t size;
>> + struct vpci_bar *bar = data;
>> +
>> + size = PCI_REBAR_CTRL_SIZE(val);
>> + if ( bar->enabled )
>> + {
>> + if ( size == bar->size )
>> + return;
>> +
>> + /*
>
> Indentation
>
>> + * Refuse to resize a BAR while memory decoding is enabled, as
>> + * otherwise the size of the mapped region in the p2m would become
>> + * stale with the newly set BAR size, and the position of the BAR
>> + * would be reset to undefined. Note the PCIe specification also
>> + * forbids resizing a BAR with memory decoding enabled.
>> + */
>> + gprintk(XENLOG_ERR,
>> + "%pp: refuse to resize BAR with memory decoding enabled\n",
>> + &pdev->sbdf);
>> + return;
>> + }
>
> Nit: just realized this could be made shorter:
>
> if ( bar->enabled )
> {
> /*
> * Refuse to resize a BAR while memory decoding is enabled, as
> * otherwise the size of the mapped region in the p2m would become
> * stale with the newly set BAR size, and the position of the BAR
> * would be reset to undefined. Note the PCIe specification also
> * forbids resizing a BAR with memory decoding enabled.
> */
> if ( size != bar->size )
> gprintk(XENLOG_ERR,
> "%pp: refuse to resize BAR with memory decoding enabled\n",
> &pdev->sbdf);
>
> return;
> }
>
>> +
>> + if ( !((size >> PCI_REBAR_SIZE_BIAS) &
>> + MASK_EXTR(pci_conf_read32(pdev->sbdf,
>> + reg - PCI_REBAR_CTRL + PCI_REBAR_CAP),
>> + PCI_REBAR_CAP_SIZES)) )
>
> Would it be possible to cache this information. It's my understand
> the supported sizes won't change, and hence Xen could read and cache
> them in init_rebar() to avoid repeated reads to the register on every
> access?
Thanks, I will change in next version.
Then I think I need to add a parameter to struct vpci_bar.
Maybe I can name it "resizable_sizes" ?
>
>> + gprintk(XENLOG_WARNING,
>> + "%pp: new size %#lx is not supported by hardware\n",
>> + &pdev->sbdf, size);
>> +
>> + bar->size = size;
>> + bar->addr = 0;
>> + bar->guest_addr = 0;
>> + pci_conf_write32(pdev->sbdf, reg, val);
>> +}
>> +
>> +static int cf_check init_rebar(struct pci_dev *pdev)
>> +{
>> + uint32_t ctrl;
>> + unsigned int rebar_offset, nbars;
>> +
>> + rebar_offset = pci_find_ext_capability(pdev->sbdf,
>> PCI_EXT_CAP_ID_REBAR);
>
> You can do the init at definition:
>
> uint32_t ctrl;
> unsigned int nbars;
> unsigned int rebar_offset = pci_find_ext_capability(pdev->sbdf,
> PCI_EXT_CAP_ID_REBAR);
>
>
>> +
>> + if ( !rebar_offset )
>> + return 0;
>> +
>> + if ( !is_hardware_domain(pdev->domain) )
>> + {
>> + printk("ReBar is not supported for domUs\n");
>
> This needs a bit more information IMO:
>
> printk(XENLOG_ERR
> "%pd %pp: resizable BAR capability not supported for unprivileged
> domains\n",
> pdev->domain, &pdev->sbdf);
OK, will change.
If the length of code of printing more than 80 characters in one line, is it
fine?
>
> I wonder if this should instead be an XSM check, but that would
> require a new XSM hook to process permissions for PCI capabilities.
>
>> + return -EOPNOTSUPP;
>> + }
>> +
>> + ctrl = pci_conf_read32(pdev->sbdf, rebar_offset + PCI_REBAR_CTRL);
>> + nbars = MASK_EXTR(ctrl, PCI_REBAR_CTRL_NBAR_MASK);
>> +
>> + for ( unsigned int i = 0; i < nbars; i++, rebar_offset +=
>> PCI_REBAR_CTRL )
>> + {
>> + int rc;
>> + unsigned int index;
>> + struct vpci_bar *bars = pdev->vpci->header.bars;
>> +
>> + index = pci_conf_read32(pdev->sbdf, rebar_offset + PCI_REBAR_CTRL) &
>> + PCI_REBAR_CTRL_BAR_IDX;
>
> You could initialize index at definition.
>
>> +
>> + if ( index >= PCI_HEADER_NORMAL_NR_BARS )
>> + {
>> + /*
>> + * TODO: for failed pathes, need to hide ReBar capability
>> + * from hardware domain instead of returning an error.
>> + */
>> + printk("%pp: BAR number %u in REBAR_CTRL register is too big\n",
>> + &pdev->sdf, index);
>
> XENLOG_ERR, plus we could print the domain the device was assigned to
> (pdev->domain).
>
>> + return -EINVAL;
>> + }
>> +
>> + if ( bars[index].type != VPCI_BAR_MEM64_LO &&
>> + bars[index].type != VPCI_BAR_MEM32 )
>> + {
>> + printk("%pp: BAR%u is not in memory space\n", &pdev->sbdf,
>> index);
>> + return -EINVAL;
>> + }
>> +
>> + rc = vpci_add_register(pdev->vpci, vpci_hw_read32, vpci_hw_write32,
>> + rebar_offset + PCI_REBAR_CAP, 4, NULL);
>> + if ( rc )
>> + {
>> + printk("%pp: add register for PCI_REBAR_CAP failed (rc=%d)\n",
>> + &pdev->sbdf, rc);
>> + return rc;
>> + }
>> +
>> + rc = vpci_add_register(pdev->vpci, vpci_hw_read32, rebar_ctrl_write,
>> + rebar_offset + PCI_REBAR_CTRL, 4,
>> + &bars[index]);
>> + if ( rc )
>> + {
>> + printk("%pp: add register for PCI_REBAR_CTRL failed %d\n",
>> + &pdev->sbdf, rc);
>> + return rc;
>> + }
>
> All the log messages above need the XENLOG_ERR prefix, plus possibly
> printing the assigned domain.
Will change according to your all comments, thank you!
>
> Thanks, Roger.
--
Best regards,
Jiqian Chen.
|
![]() |
Lists.xenproject.org is hosted with RackSpace, monitoring our |