[PATCH v3 10/11] vpci/msi: Free MSI resources when init_msi() fails

Jiqian Chen posted 11 patches 6 months, 1 week ago
There is a newer version of this series
[PATCH v3 10/11] vpci/msi: Free MSI resources when init_msi() fails
Posted by Jiqian Chen 6 months, 1 week ago
When init_msi() fails, the previous new changes will hide MSI
capability, it can't rely on vpci_deassign_device() to remove
all MSI related resources anymore, those resources must be
cleaned up in failure path of init_msi.

To do that, add a new function to free MSI resources.

Signed-off-by: Jiqian Chen <Jiqian.Chen@amd.com>
---
cc: "Roger Pau Monné" <roger.pau@citrix.com>
---
v2->v3 changes:
* Remove all fail path, and use fini_msi() hook instead.
* Change the method to calculating the size of msi registers.

v1->v2 changes:
* Added a new function fini_msi to free all MSI resources instead of using an array to record registered registers.

Best regards,
Jiqian Chen.
---
 xen/drivers/vpci/msi.c | 28 +++++++++++++++++++++++++++-
 1 file changed, 27 insertions(+), 1 deletion(-)

diff --git a/xen/drivers/vpci/msi.c b/xen/drivers/vpci/msi.c
index ea7dc0c060ea..18b06b789827 100644
--- a/xen/drivers/vpci/msi.c
+++ b/xen/drivers/vpci/msi.c
@@ -193,6 +193,32 @@ static void cf_check mask_write(
     msi->mask = val;
 }
 
+static void fini_msi(struct pci_dev *pdev)
+{
+    unsigned int end, size;
+    struct vpci *vpci = pdev->vpci;
+    const unsigned int msi_pos = pdev->msi_pos;
+
+    if ( !msi_pos || !vpci->msi )
+        return;
+
+    if ( vpci->msi->masking )
+        end = msi_pending_bits_reg(msi_pos, vpci->msi->address64);
+    else
+        end = msi_mask_bits_reg(msi_pos, vpci->msi->address64) - 2;
+
+    size = end - msi_control_reg(msi_pos);
+
+    /*
+     * Remove all possible registered registers except capability ID
+     * register if guest and next capability pointer register, which
+     * will be removed in mask function.
+     */
+    vpci_remove_registers(vpci, msi_control_reg(msi_pos), size);
+    xfree(vpci->msi);
+    vpci->msi = NULL;
+}
+
 static int cf_check init_msi(struct pci_dev *pdev)
 {
     unsigned int pos = pdev->msi_pos;
@@ -270,7 +296,7 @@ static int cf_check init_msi(struct pci_dev *pdev)
 
     return 0;
 }
-REGISTER_VPCI_LEGACY_CAP(PCI_CAP_ID_MSI, init_msi, NULL);
+REGISTER_VPCI_LEGACY_CAP(PCI_CAP_ID_MSI, init_msi, fini_msi);
 
 void vpci_dump_msi(void)
 {
-- 
2.34.1


Re: [PATCH v3 10/11] vpci/msi: Free MSI resources when init_msi() fails
Posted by Roger Pau Monné 5 months, 3 weeks ago
On Mon, Apr 21, 2025 at 02:19:02PM +0800, Jiqian Chen wrote:
> When init_msi() fails, the previous new changes will hide MSI
> capability, it can't rely on vpci_deassign_device() to remove
> all MSI related resources anymore, those resources must be
> cleaned up in failure path of init_msi.
> 
> To do that, add a new function to free MSI resources.
> 
> Signed-off-by: Jiqian Chen <Jiqian.Chen@amd.com>
> ---
> cc: "Roger Pau Monné" <roger.pau@citrix.com>
> ---
> v2->v3 changes:
> * Remove all fail path, and use fini_msi() hook instead.
> * Change the method to calculating the size of msi registers.
> 
> v1->v2 changes:
> * Added a new function fini_msi to free all MSI resources instead of using an array to record registered registers.
> 
> Best regards,
> Jiqian Chen.
> ---
>  xen/drivers/vpci/msi.c | 28 +++++++++++++++++++++++++++-
>  1 file changed, 27 insertions(+), 1 deletion(-)
> 
> diff --git a/xen/drivers/vpci/msi.c b/xen/drivers/vpci/msi.c
> index ea7dc0c060ea..18b06b789827 100644
> --- a/xen/drivers/vpci/msi.c
> +++ b/xen/drivers/vpci/msi.c
> @@ -193,6 +193,32 @@ static void cf_check mask_write(
>      msi->mask = val;
>  }
>  
> +static void fini_msi(struct pci_dev *pdev)
> +{
> +    unsigned int end, size;
> +    struct vpci *vpci = pdev->vpci;
> +    const unsigned int msi_pos = pdev->msi_pos;
> +
> +    if ( !msi_pos || !vpci->msi )
> +        return;
> +
> +    if ( vpci->msi->masking )
> +        end = msi_pending_bits_reg(msi_pos, vpci->msi->address64);
> +    else
> +        end = msi_mask_bits_reg(msi_pos, vpci->msi->address64) - 2;
> +
> +    size = end - msi_control_reg(msi_pos);
> +
> +    /*
> +     * Remove all possible registered registers except capability ID
> +     * register if guest and next capability pointer register, which
> +     * will be removed in mask function.

The above text seems very convoluted.  I prefer re-using the same
comment that you had in the ReBAR cleanup helper:

    /*
     * Remove all possible registered registers except header.
     * Header register will be removed in mask function.
     */

> +     */
> +    vpci_remove_registers(vpci, msi_control_reg(msi_pos), size);
> +    xfree(vpci->msi);
> +    vpci->msi = NULL;

XFREE(vpci->msi);

Will be more compact.

Thanks, Roger.