diff options
| author | Bjorn Helgaas <bhelgaas@google.com> | 2026-10-21 13:20:28 -0500 |
|---|---|---|
| committer | Bjorn Helgaas <bhelgaas@google.com> | 2026-10-21 13:20:28 -0500 |
| commit | 01aab1b5b9e6064fdf2edcd6446057f0705d882a (patch) | |
| tree | 79d595e49deaee2d580936bc9108e73ef6debcf2 /drivers | |
| parent | ef12cddc4c4762650b0e1f239790179f29a5f480 (diff) | |
| parent | a5401ce3ab2356b7861f53d09205008b7a49d57e (diff) | |
Merge branch 'pci/endpoint'
- Pass the correct physical and virtual endpoint function numbers when
setting translation of inbound memory windows (Koichiro Den)
- Make embedded doorbell IRQ non-shared, since currently only the first EPF
can allocate doorbells (Koichiro Den)
- MSI doorbells currently target the first EPF attached to an EPC; let
other EPFs try the embedded doorbell instead of failing when they can't
use the MSI doorbell (Koichiro Den)
- Serialize scan of vNTB virtual PCI bus with other PCI topology changes
(Koichiro Den)
- Manage vNTB lifetimes to avoid leaking virtual devices and buses and
allow removal (Koichiro Den)
- Track vNTB link state from both sides so both see the same effective link
state (Koichiro Den)
- Add local epf pointer to shorten repeated ntp->epf accesses (Koichiro
Den)
- Configure vNTB MSI-X when supported to avoid IOMMU faults and failure to
deliver doorbell interrupts on RC (Koichiro Den)
* pci/endpoint:
PCI: endpoint: pci-epf-vntb: Honor MSI-X selection
PCI: endpoint: pci-epf-vntb: Use a local epf pointer in interrupt setup
PCI: endpoint: pci-epf-vntb: Track link state from both sides
PCI: endpoint: pci-epf-vntb: Manage virtual NTB and PCI bus lifetime
PCI: endpoint: pci-epf-vntb: Serialize virtual PCI bus scan
PCI: endpoint: pci-ep-msi: Let non-first EPFs use embedded doorbells
PCI: endpoint: pci-ep-msi: Make embedded doorbell IRQ exclusive
PCI: endpoint: pci-epf-vntb: Pass PF/VF number when BAR programming
Diffstat (limited to 'drivers')
| -rw-r--r-- | drivers/pci/endpoint/functions/pci-epf-vntb.c | 243 | ||||
| -rw-r--r-- | drivers/pci/endpoint/pci-ep-msi.c | 40 |
2 files changed, 220 insertions, 63 deletions
diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index c3caec927d74..f77e28f2fe96 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -41,6 +41,7 @@ #include <linux/delay.h> #include <linux/io.h> #include <linux/module.h> +#include <linux/mutex.h> #include <linux/slab.h> #include <linux/pci-ep-msi.h> @@ -132,7 +133,7 @@ struct epf_ntb_ctrl { } __packed; struct epf_ntb { - struct ntb_dev ntb; + struct ntb_dev *ntb; struct pci_epf *epf; struct config_group group; @@ -147,7 +148,11 @@ struct epf_ntb { u16 vntb_pid; u16 vntb_vid; - bool linkup; + /* Serialize HOST and VHOST link state changes. */ + struct mutex link_lock; + bool host_link_enabled; + bool vhost_link_enabled; + bool peer_msix; /* * True when doorbells are interrupt-driven (MSI or embedded), false @@ -155,6 +160,7 @@ struct epf_ntb { */ bool msi_doorbell; u32 spad_size; + u32 msix_table_offset; enum pci_barno epf_ntb_bar[VNTB_BAR_NUM]; @@ -166,10 +172,15 @@ struct epf_ntb { void __iomem *vpci_mw_addr[MAX_MW]; struct delayed_work cmd_handler; + struct pci_bus *vpci_bus; }; #define to_epf_ntb(epf_group) container_of((epf_group), struct epf_ntb, group) -#define ntb_ndev(__ntb) container_of(__ntb, struct epf_ntb, ntb) + +static struct epf_ntb *ntb_ndev(struct ntb_dev *ntb) +{ + return ntb->pdev->sysdata; +} static struct pci_epf_header epf_ntb_header = { .vendorid = PCI_ANY_ID, @@ -178,24 +189,58 @@ static struct pci_epf_header epf_ntb_header = { .interrupt_pin = PCI_INTERRUPT_INTA, }; +static void epf_ntb_update_link(struct epf_ntb *ntb) +{ + u16 link_status = READ_ONCE(ntb->reg->link_status); + + /* The link is usable only after both sides have enabled it. */ + if (ntb->host_link_enabled && ntb->vhost_link_enabled) + link_status |= LINK_STATUS_UP; + else + link_status &= ~LINK_STATUS_UP; + + WRITE_ONCE(ntb->reg->link_status, link_status); +} + +static int epf_ntb_notify_link(struct ntb_dev *ndev) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + struct pci_epf *epf = ntb->epf; + unsigned int irq_type; + int ret; + + ntb_link_event(ndev); + + irq_type = ntb->peer_msix ? PCI_IRQ_MSIX : PCI_IRQ_MSI; + ret = pci_epc_raise_irq(epf->epc, epf->func_no, epf->vfunc_no, + irq_type, EPF_IRQ_LINK + 1); + if (ret) + dev_err(&epf->dev, "Failed to raise link event IRQ: %d\n", ret); + + return ret; +} + /** - * epf_ntb_link_up() - Raise link_up interrupt to Virtual Host (VHOST) + * epf_ntb_set_link_enabled() - Set HOST link enablement * @ntb: NTB device that facilitates communication between HOST and VHOST - * @link_up: true or false indicating Link is UP or Down - * - * Once NTB function in HOST invoke ntb_link_enable(), - * this NTB function driver will trigger a link event to VHOST. + * @enabled: true when the HOST has enabled the link * * Returns: Zero for success, or an error code in case of failure */ -static int epf_ntb_link_up(struct epf_ntb *ntb, bool link_up) +static int epf_ntb_set_link_enabled(struct epf_ntb *ntb, bool enabled) { - if (link_up) - ntb->reg->link_status |= LINK_STATUS_UP; - else - ntb->reg->link_status &= ~LINK_STATUS_UP; + bool notify; + + scoped_guard(mutex, &ntb->link_lock) { + notify = ntb->host_link_enabled != enabled && + ntb->vhost_link_enabled; + ntb->host_link_enabled = enabled; + epf_ntb_update_link(ntb); + } + + if (notify) + return epf_ntb_notify_link(ntb->ntb); - ntb_link_event(&ntb->ntb); return 0; } @@ -271,6 +316,7 @@ static void epf_ntb_teardown_mw(struct epf_ntb *ntb, u32 mw) */ static void epf_ntb_cmd_handler(struct work_struct *work) { + const struct pci_epc_features *epc_features; struct epf_ntb_ctrl *ctrl; u32 command, argument; struct epf_ntb *ntb; @@ -284,7 +330,7 @@ static void epf_ntb_cmd_handler(struct work_struct *work) i++) { if (ntb->epf_db[i]) { atomic64_or(1 << (i - EPF_IRQ_DB_START), &ntb->db); - ntb_db_event(&ntb->ntb, i - EPF_IRQ_DB_START); + ntb_db_event(ntb->ntb, i - EPF_IRQ_DB_START); ntb->epf_db[i] = 0; } } @@ -303,6 +349,15 @@ static void epf_ntb_cmd_handler(struct work_struct *work) switch (command) { case COMMAND_CONFIGURE_DOORBELL: + epc_features = pci_epc_get_features(ntb->epf->epc, + ntb->epf->func_no, + ntb->epf->vfunc_no); + if (((argument & MSIX_ENABLE) && !epc_features->msix_capable) || + (!(argument & MSIX_ENABLE) && !epc_features->msi_capable)) { + ctrl->command_status = COMMAND_STATUS_ERROR; + break; + } + ntb->peer_msix = argument & MSIX_ENABLE; ctrl->command_status = COMMAND_STATUS_OK; break; case COMMAND_TEARDOWN_DOORBELL: @@ -320,16 +375,14 @@ static void epf_ntb_cmd_handler(struct work_struct *work) ctrl->command_status = COMMAND_STATUS_OK; break; case COMMAND_LINK_UP: - ntb->linkup = true; - ret = epf_ntb_link_up(ntb, true); + ret = epf_ntb_set_link_enabled(ntb, true); if (ret < 0) ctrl->command_status = COMMAND_STATUS_ERROR; else ctrl->command_status = COMMAND_STATUS_OK; goto reset_handler; case COMMAND_LINK_DOWN: - ntb->linkup = false; - ret = epf_ntb_link_up(ntb, false); + ret = epf_ntb_set_link_enabled(ntb, false); if (ret < 0) ctrl->command_status = COMMAND_STATUS_ERROR; else @@ -348,12 +401,18 @@ reset_handler: static irqreturn_t epf_ntb_doorbell_handler(int irq, void *data) { struct epf_ntb *ntb = data; + struct ntb_dev *ndev; int i; + /* Pair with smp_store_release() in pci_vntb_probe() */ + ndev = smp_load_acquire(&ntb->ntb); + if (!ndev) + return IRQ_HANDLED; + for (i = EPF_IRQ_DB_START; i < ntb->db_count; i++) if (irq == ntb->epf->db_msg[i].virq) { atomic64_or(1 << (i - EPF_IRQ_DB_START), &ntb->db); - ntb_db_event(&ntb->ntb, i - EPF_IRQ_DB_START); + ntb_db_event(ndev, i - EPF_IRQ_DB_START); } return IRQ_HANDLED; @@ -440,8 +499,9 @@ static void epf_ntb_config_spad_bar_free(struct epf_ntb *ntb) * @ntb: NTB device that facilitates communication between HOST and VHOST * * Allocate the Local Memory mentioned in the above diagram. The size of - * CONFIG REGION is sizeof(struct epf_ntb_ctrl) and size of SCRATCHPAD REGION - * is obtained from "spad-count" configfs entry. + * CONFIG REGION is sizeof(struct epf_ntb_ctrl), plus space for the MSI-X + * Table and PBA if supported. The size of SCRATCHPAD REGION is obtained + * from "spad-count" configfs entry. * * Returns: Zero for success, or an error code in case of failure */ @@ -462,6 +522,12 @@ static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb) spad_count = ntb->spad_count; ctrl_size = ALIGN(sizeof(struct epf_ntb_ctrl), sizeof(u32)); + if (epc_features->msix_capable) { + ntb->msix_table_offset = ALIGN(ctrl_size, 8); + ctrl_size = ntb->msix_table_offset + + ntb->db_count * PCI_MSIX_ENTRY_SIZE + + BITS_TO_U64(ntb->db_count) * sizeof(u64); + } spad_size = 2 * spad_count * sizeof(u32); base = pci_epf_alloc_space(epf, ctrl_size + spad_size, @@ -502,12 +568,14 @@ static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb) static int epf_ntb_configure_interrupt(struct epf_ntb *ntb) { const struct pci_epc_features *epc_features; + struct pci_epf *epf = ntb->epf; struct device *dev; int ret; - dev = &ntb->epf->dev; + dev = &epf->dev; - epc_features = pci_epc_get_features(ntb->epf->epc, ntb->epf->func_no, ntb->epf->vfunc_no); + epc_features = pci_epc_get_features(epf->epc, epf->func_no, + epf->vfunc_no); if (!(epc_features->msix_capable || epc_features->msi_capable)) { dev_err(dev, "MSI or MSI-X is required for doorbell\n"); @@ -521,16 +589,24 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb) } if (epc_features->msi_capable) { - ret = pci_epc_set_msi(ntb->epf->epc, - ntb->epf->func_no, - ntb->epf->vfunc_no, - 16); + ret = pci_epc_set_msi(epf->epc, epf->func_no, epf->vfunc_no, 16); if (ret) { dev_err(dev, "MSI configuration failed\n"); return ret; } } + if (epc_features->msix_capable) { + ret = pci_epc_set_msix(epf->epc, epf->func_no, epf->vfunc_no, + ntb->db_count, + ntb->epf_ntb_bar[BAR_CONFIG], + ntb->msix_table_offset); + if (ret) { + dev_err(dev, "MSI-X configuration failed\n"); + return ret; + } + } + return 0; } @@ -985,7 +1061,6 @@ static int epf_ntb_epc_init(struct epf_ntb *ntb) } INIT_DELAYED_WORK(&ntb->cmd_handler, epf_ntb_cmd_handler); - queue_work(kpcintb_workqueue, &ntb->cmd_handler.work); atomic64_set(&ntb->peer_db_pending, 0); enable_work(&ntb->peer_db_work); @@ -1332,14 +1407,20 @@ static int vpci_scan_bus(void *sysdata) struct pci_bus *vpci_bus; struct epf_ntb *ndev = sysdata; + pci_lock_rescan_remove(); + vpci_bus = pci_scan_bus(ndev->vbus_number, &vpci_ops, sysdata); if (!vpci_bus) { pr_err("create pci bus failed\n"); + pci_unlock_rescan_remove(); return -EINVAL; } + ndev->vpci_bus = vpci_bus; pci_bus_add_devices(vpci_bus); + pci_unlock_rescan_remove(); + return 0; } @@ -1420,14 +1501,15 @@ static int vntb_epf_mw_set_trans(struct ntb_dev *ndev, int pidx, int idx, int ret; struct device *dev; - dev = &ntb->ntb.dev; + dev = &ndev->dev; barno = ntb->epf_ntb_bar[BAR_MW1 + idx]; epf_bar = &ntb->epf->bar[barno]; epf_bar->phys_addr = addr; epf_bar->barno = barno; epf_bar->size = size; - ret = pci_epc_set_bar(ntb->epf->epc, 0, 0, epf_bar); + ret = pci_epc_set_bar(ntb->epf->epc, ntb->epf->func_no, + ntb->epf->vfunc_no, epf_bar); if (ret) { dev_err(dev, "failure set mw trans\n"); return ret; @@ -1455,11 +1537,29 @@ static int vntb_epf_peer_mw_get_addr(struct ntb_dev *ndev, int idx, return 0; } +static int vntb_epf_set_link_enabled(struct ntb_dev *ndev, bool enabled) +{ + struct epf_ntb *ntb = ntb_ndev(ndev); + bool notify; + + scoped_guard(mutex, &ntb->link_lock) { + notify = ntb->vhost_link_enabled != enabled && + ntb->host_link_enabled; + ntb->vhost_link_enabled = enabled; + epf_ntb_update_link(ntb); + } + + if (notify) + return epf_ntb_notify_link(ndev); + + return 0; +} + static int vntb_epf_link_enable(struct ntb_dev *ntb, enum ntb_speed max_speed, enum ntb_width max_width) { - return 0; + return vntb_epf_set_link_enabled(ntb, true); } static u32 vntb_epf_spad_read(struct ntb_dev *ndev, int idx) @@ -1512,6 +1612,7 @@ static void vntb_epf_peer_db_work(struct work_struct *work) struct epf_ntb *ntb = container_of(work, struct epf_ntb, peer_db_work); struct pci_epf *epf = ntb->epf; unsigned int budget = VNTB_PEER_DB_WORK_BUDGET; + unsigned int irq_type; u8 func_no, vfunc_no; unsigned int db_bit; u32 interrupt_num; @@ -1523,6 +1624,7 @@ static void vntb_epf_peer_db_work(struct work_struct *work) func_no = epf->func_no; vfunc_no = epf->vfunc_no; + irq_type = ntb->peer_msix ? PCI_IRQ_MSIX : PCI_IRQ_MSI; /* * Drain doorbells from peer_db_pending in snapshots (atomic64_xchg()). @@ -1536,7 +1638,7 @@ static void vntb_epf_peer_db_work(struct work_struct *work) while (db_bits) { /* - * pci_epc_raise_irq() for MSI expects a 1-based + * pci_epc_raise_irq() for MSI/MSI-X expects a 1-based * interrupt number. The first usable doorbell starts * at EPF_IRQ_DB_START in the legacy slot layout. * @@ -1556,9 +1658,9 @@ static void vntb_epf_peer_db_work(struct work_struct *work) db_bits &= ~BIT_ULL(db_bit); ret = pci_epc_raise_irq(epf->epc, func_no, vfunc_no, - PCI_IRQ_MSI, interrupt_num); + irq_type, interrupt_num); if (ret) - dev_err(&ntb->ntb.dev, + dev_err(&epf->dev, "Failed to raise IRQ for interrupt_num %u: %d\n", interrupt_num, ret); } @@ -1619,7 +1721,7 @@ static u64 vntb_epf_link_is_up(struct ntb_dev *ndev, { struct epf_ntb *ntb = ntb_ndev(ndev); - return ntb->reg->link_status; + return READ_ONCE(ntb->reg->link_status); } static int vntb_epf_db_clear_mask(struct ntb_dev *ndev, u64 db_bits) @@ -1637,7 +1739,7 @@ static int vntb_epf_db_clear(struct ntb_dev *ndev, u64 db_bits) static int vntb_epf_link_disable(struct ntb_dev *ntb) { - return 0; + return vntb_epf_set_link_enabled(ntb, false); } static struct device *vntb_epf_get_dma_dev(struct ntb_dev *ndev) @@ -1676,13 +1778,18 @@ static const struct ntb_dev_ops vntb_epf_ops = { static int pci_vntb_probe(struct pci_dev *pdev, const struct pci_device_id *id) { - int ret; struct epf_ntb *ndev = (struct epf_ntb *)pdev->sysdata; struct device *dev = &pdev->dev; + struct ntb_dev *ntb; + int ret; + + ntb = devm_kzalloc(dev, sizeof(*ntb), GFP_KERNEL); + if (!ntb) + return -ENOMEM; - ndev->ntb.pdev = pdev; - ndev->ntb.topo = NTB_TOPO_NONE; - ndev->ntb.ops = &vntb_epf_ops; + ntb->pdev = pdev; + ntb->topo = NTB_TOPO_NONE; + ntb->ops = &vntb_epf_ops; ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); if (ret) { @@ -1690,16 +1797,41 @@ static int pci_vntb_probe(struct pci_dev *pdev, const struct pci_device_id *id) return ret; } - ret = ntb_register_device(&ndev->ntb); + ret = ntb_register_device(ntb); if (ret) { dev_err(dev, "Failed to register NTB device\n"); return ret; } + /* Publish after ntb_register_device() succeeds */ + smp_store_release(&ndev->ntb, ntb); + queue_delayed_work(kpcintb_workqueue, &ndev->cmd_handler, 0); + dev_dbg(dev, "PCI Virtual NTB driver loaded\n"); return 0; } +static void pci_vntb_remove(struct pci_dev *pdev) +{ + struct epf_ntb *ndev = pdev->sysdata; + struct ntb_dev *ntb; + unsigned int i; + + /* Stop the work reader, then close and drain the IRQ gate */ + cancel_delayed_work_sync(&ndev->cmd_handler); + + ntb = xchg(&ndev->ntb, NULL); + + if (ndev->msi_doorbell) + for (i = 0; i < ndev->db_count; i++) { + if (epf_ntb_db_irq_is_duplicated(ndev->epf, i)) + continue; + synchronize_irq(ndev->epf->db_msg[i].virq); + } + + ntb_unregister_device(ntb); +} + static struct pci_device_id pci_vntb_table[] = { { PCI_DEVICE(0xffff, 0xffff), @@ -1711,6 +1843,7 @@ static struct pci_driver vntb_pci_driver = { .name = "pci-vntb", .id_table = pci_vntb_table, .probe = pci_vntb_probe, + .remove = pci_vntb_remove, }; /* ============ PCIe EPF Driver Bind ====================*/ @@ -1749,6 +1882,10 @@ static int epf_ntb_bind(struct pci_epf *epf) goto err_bar_alloc; } + ntb->host_link_enabled = false; + ntb->vhost_link_enabled = false; + ntb->reg->link_status = 0; + ret = epf_ntb_epc_init(ntb); if (ret) { dev_err(dev, "Failed to initialize EPC\n"); @@ -1791,12 +1928,25 @@ err_bar_alloc: */ static void epf_ntb_unbind(struct pci_epf *epf) { + struct pci_host_bridge *bridge; struct epf_ntb *ntb = epf_get_drvdata(epf); + pci_unregister_driver(&vntb_pci_driver); + + if (ntb->vpci_bus) { + bridge = to_pci_host_bridge(ntb->vpci_bus->bridge); + + pci_lock_rescan_remove(); + pci_stop_root_bus(ntb->vpci_bus); + pci_remove_root_bus(ntb->vpci_bus); + ntb->vpci_bus = NULL; + pci_unlock_rescan_remove(); + + pci_free_host_bridge(bridge); + } + epf_ntb_epc_cleanup(ntb); epf_ntb_config_spad_bar_free(ntb); - - pci_unregister_driver(&vntb_pci_driver); } // EPF driver probe @@ -1821,7 +1971,7 @@ static int epf_ntb_probe(struct pci_epf *epf, { struct epf_ntb *ntb; struct device *dev; - int i; + int ret, i; dev = &epf->dev; @@ -1832,6 +1982,9 @@ static int epf_ntb_probe(struct pci_epf *epf, epf->header = &epf_ntb_header; ntb->epf = epf; ntb->vbus_number = 0xff; + ret = devm_mutex_init(dev, &ntb->link_lock); + if (ret) + return ret; INIT_WORK(&ntb->peer_db_work, vntb_epf_peer_db_work); disable_work(&ntb->peer_db_work); diff --git a/drivers/pci/endpoint/pci-ep-msi.c b/drivers/pci/endpoint/pci-ep-msi.c index 0855c7930abb..5ea7eef9794f 100644 --- a/drivers/pci/endpoint/pci-ep-msi.c +++ b/drivers/pci/endpoint/pci-ep-msi.c @@ -10,7 +10,6 @@ #include <linux/cleanup.h> #include <linux/device.h> #include <linux/export.h> -#include <linux/interrupt.h> #include <linux/irqdomain.h> #include <linux/module.h> #include <linux/msi.h> @@ -112,8 +111,11 @@ static int pci_epf_alloc_doorbell_embedded(struct pci_epf *epf, u16 num_db) * * Still, pci_epf_alloc_doorbell() allows requesting multiple doorbells. * For such backends we replicate the same address/data for each entry - * and mark the IRQ as shared (IRQF_SHARED). Consumers must treat them - * as equivalent "kick" doorbells. + * as equivalent "kick" doorbells. Consumers must request each distinct + * IRQ only once. + * + * Keep the IRQ exclusive because the source cannot identify which EPF + * rang the doorbell. */ for (i = 0; i < num_db; i++) msg[i] = (struct pci_epf_doorbell_msg) { @@ -121,7 +123,6 @@ static int pci_epf_alloc_doorbell_embedded(struct pci_epf *epf, u16 num_db) .msg.address_hi = (u32)(addr >> 32), .msg.data = doorbell->u.db_mmio.data, .virq = doorbell->u.db_mmio.irq, - .irq_flags = IRQF_SHARED, .type = PCI_EPF_DOORBELL_EMBEDDED, .bar = doorbell->bar, .offset = (doorbell->bar == NO_BAR) ? 0 : @@ -198,28 +199,31 @@ static int pci_epf_alloc_doorbell_msi(struct pci_epf *epf, u16 num_db) int pci_epf_alloc_doorbell(struct pci_epf *epf, u16 num_db) { struct pci_epc *epc = epf->epc; + struct pci_epf *first_epf; struct device *dev = &epf->dev; int ret; - /* TODO: Multi-EPF support */ - if (list_first_entry_or_null(&epc->pci_epf, struct pci_epf, list) != epf) { - dev_err(dev, "Doorbell doesn't support multiple EPF\n"); - return -EINVAL; - } - if (epf->db_msg) return -EBUSY; - ret = pci_epf_alloc_doorbell_msi(epf, num_db); - if (!ret) - return 0; - /* - * Fall back to embedded doorbell only when platform MSI is unavailable - * for this EPC. + * The MSI-backed doorbell path currently targets the first EPF attached + * to the EPC. Let non-first EPFs try the embedded doorbell instead. */ - if (ret != -ENODEV) - return ret; + first_epf = list_first_entry_or_null(&epc->pci_epf, struct pci_epf, + list); + if (first_epf == epf) { + ret = pci_epf_alloc_doorbell_msi(epf, num_db); + if (!ret) + return 0; + + /* + * Fall back to embedded doorbell only when platform MSI is + * unavailable for this EPC. + */ + if (ret != -ENODEV) + return ret; + } ret = pci_epf_alloc_doorbell_embedded(epf, num_db); if (ret) { |
