[PATCH 1/3] PCI: endpoint: Support hardware-owned MSI-X table and PBA
Koichiro Den <[email protected]>
| Newsgroups | org.infradead.lists.linux-nvme,dev.linux.lists.ntb,org.infradead.lists.linux-arm-kernel,org.infradead.lists.linux-rockchip,org.kernel.vger.linux-kernel,org.kernel.vger.linux-pci |
|---|---|
| Message-ID | <[email protected]> |
Some endpoint controllers expose the MSI-X table and Pending Bit Array (PBA) in fixed, hardware-owned BAR regions. The EPC set_msix() callback currently receives only the table BAR and offset and assumes that the PBA immediately follows the table in the same BAR. It cannot describe a fixed layout whose table and PBA have independent locations. Add struct pci_epc_msix_layout and pass the full layout to pci_epc_set_msix(). Leave layout selection to EPF implementations. For instance, pci-epf-ntb reads the MSI-X Table from its own BAR. Using a fixed layout there would require a way to read hardware-owned Table entries. Add pci_epc_get_hw_msix_layout() for EPFs that want the fixed layout. Update the existing callers and validate the supplied layout. Signed-off-by: Koichiro Den <[email protected]> --- drivers/nvme/target/pci-epf.c | 16 ++- .../pci/controller/cadence/pcie-cadence-ep.c | 9 +- .../pci/controller/dwc/pcie-designware-ep.c | 7 +- drivers/pci/endpoint/functions/pci-epf-ntb.c | 30 +++--- drivers/pci/endpoint/functions/pci-epf-test.c | 17 +-- drivers/pci/endpoint/pci-epc-core.c | 102 +++++++++++++++++- include/linux/pci-epc.h | 25 ++++- 7 files changed, 168 insertions(+), 38 deletions(-) diff --git a/drivers/nvme/target/pci-epf.c b/drivers/nvme/target/pci-epf.c index 4e9db96ebfec..37182f6d29b1 100644 --- a/drivers/nvme/target/pci-epf.c +++ b/drivers/nvme/target/pci-epf.c @@ -201,7 +201,7 @@ struct nvmet_pci_epf { const struct pci_epc_features *epc_features; void *reg_bar; - size_t msix_table_offset; + struct pci_epc_msix_layout msix_layout; unsigned int irq_type; unsigned int nr_vectors; @@ -2187,8 +2187,14 @@ static int nvmet_pci_epf_configure_bar(struct nvmet_pci_epf *nvme_epf) size_t pba_size; msix_table_size = PCI_MSIX_ENTRY_SIZE * epf->msix_interrupts; - nvme_epf->msix_table_offset = reg_size; - pba_size = ALIGN(DIV_ROUND_UP(epf->msix_interrupts, 8), 8); + pba_size = BITS_TO_U64(epf->msix_interrupts) * sizeof(u64); + + nvme_epf->msix_layout.table_bar = BAR_0; + nvme_epf->msix_layout.table_offset = reg_size; + nvme_epf->msix_layout.table_size = msix_table_size; + nvme_epf->msix_layout.pba_bar = BAR_0; + nvme_epf->msix_layout.pba_offset = reg_size + msix_table_size; + nvme_epf->msix_layout.pba_size = pba_size; reg_size += msix_table_size + pba_size; } @@ -2245,8 +2251,8 @@ static int nvmet_pci_epf_init_irq(struct nvmet_pci_epf *nvme_epf) /* Enable MSI-X if supported, otherwise, use MSI. */ if (epc_features->msix_capable && epf->msix_interrupts) { ret = pci_epc_set_msix(epf->epc, epf->func_no, epf->vfunc_no, - epf->msix_interrupts, BAR_0, - nvme_epf->msix_table_offset); + epf->msix_interrupts, + &nvme_epf->msix_layout); if (ret) { dev_err(&epf->dev, "Failed to configure MSI-X\n"); return ret; diff --git a/drivers/pci/controller/cadence/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c index c0e1194a936b..2b69ea88aed2 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-ep.c +++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c @@ -293,7 +293,8 @@ static int cdns_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no) } static int cdns_pcie_ep_set_msix(struct pci_epc *epc, u8 fn, u8 vfn, - u16 nr_irqs, enum pci_barno bir, u32 offset) + u16 nr_irqs, + const struct pci_epc_msix_layout *layout) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); struct cdns_pcie *pcie = &ep->pcie; @@ -311,12 +312,12 @@ static int cdns_pcie_ep_set_msix(struct pci_epc *epc, u8 fn, u8 vfn, /* Set MSI-X BAR and offset */ reg = cap + PCI_MSIX_TABLE; - val = offset | bir; + val = layout->table_offset | layout->table_bar; cdns_pcie_ep_fn_writel(pcie, fn, reg, val); - /* Set PBA BAR and offset. BAR must match MSI-X BAR */ + /* Set PBA BAR and offset */ reg = cap + PCI_MSIX_PBA; - val = (offset + (nr_irqs * PCI_MSIX_ENTRY_SIZE)) | bir; + val = layout->pba_offset | layout->pba_bar; cdns_pcie_ep_fn_writel(pcie, fn, reg, val); return 0; diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 7d2794945704..147b043589f0 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -737,7 +737,8 @@ static int dw_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no) } static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no, - u16 nr_irqs, enum pci_barno bir, u32 offset) + u16 nr_irqs, + const struct pci_epc_msix_layout *layout) { struct dw_pcie_ep *ep = epc_get_drvdata(epc); struct dw_pcie *pci = to_dw_pcie_from_ep(ep); @@ -757,11 +758,11 @@ static int dw_pcie_ep_set_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no, dw_pcie_ep_writew_dbi(ep, func_no, reg, val); reg = ep_func->msix_cap + PCI_MSIX_TABLE; - val = offset | bir; + val = layout->table_offset | layout->table_bar; dw_pcie_ep_writel_dbi(ep, func_no, reg, val); reg = ep_func->msix_cap + PCI_MSIX_PBA; - val = (offset + (nr_irqs * PCI_MSIX_ENTRY_SIZE)) | bir; + val = layout->pba_offset | layout->pba_bar; dw_pcie_ep_writel_dbi(ep, func_no, reg, val); dw_pcie_dbi_ro_wr_dis(pci); diff --git a/drivers/pci/endpoint/functions/pci-epf-ntb.c b/drivers/pci/endpoint/functions/pci-epf-ntb.c index 5314aca2188a..f3e0e1b3ffb9 100644 --- a/drivers/pci/endpoint/functions/pci-epf-ntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-ntb.c @@ -90,12 +90,11 @@ struct epf_ntb_epc { u8 vfunc_no; bool linkup; bool is_msix; - int msix_bar; u32 spad_size; struct pci_epc *epc; struct epf_ntb *epf_ntb; void __iomem *mw_addr[6]; - size_t msix_table_offset; + struct pci_epc_msix_layout msix_layout; struct epf_ntb_ctrl *reg; struct pci_epf_bar *epf_bar; enum pci_barno epf_ntb_bar[6]; @@ -475,9 +474,9 @@ static int epf_ntb_configure_msi(struct epf_ntb *ntb, * * The MSI-X address is in the MSI-X table of EP CONTROLLER 2 and * the count of doorbell is in ctrl->argument of epf_ntb_epc that is connected - * to HOST2. MSI-X table is stored memory mapped to ntb_epc->msix_bar and the - * offset is in ntb_epc->msix_table_offset. From this epf_ntb_configure_msix() - * gets the MSI-X address and data. + * to HOST2. The location of the memory-mapped MSI-X table is described by + * ntb_epc->msix_layout. From this epf_ntb_configure_msix() gets the MSI-X + * address and data. * * epf_ntb_configure_msix() also stores the MSI-X data to raise each interrupt * in db_data of the peer's control region. This helps the peer to raise @@ -505,8 +504,8 @@ static int epf_ntb_configure_msix(struct epf_ntb *ntb, ntb_epc = ntb->epc[type]; epc = ntb_epc->epc; - epf_bar = &ntb_epc->epf_bar[ntb_epc->msix_bar]; - msix_tbl = epf_bar->addr + ntb_epc->msix_table_offset; + epf_bar = &ntb_epc->epf_bar[ntb_epc->msix_layout.table_bar]; + msix_tbl = epf_bar->addr + ntb_epc->msix_layout.table_offset; peer_ntb_epc = ntb->epc[!type]; peer_barno = peer_ntb_epc->epf_ntb_bar[BAR_DB_MW1]; @@ -1036,10 +1035,14 @@ static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb, if (msix_capable) { msix_table_size = PCI_MSIX_ENTRY_SIZE * ntb->db_count; ctrl_size = ALIGN(ctrl_size, 8); - ntb_epc->msix_table_offset = ctrl_size; - ntb_epc->msix_bar = barno; - /* Align to QWORD or 8 Bytes */ - pba_size = ALIGN(DIV_ROUND_UP(ntb->db_count, 8), 8); + pba_size = BITS_TO_U64(ntb->db_count) * sizeof(u64); + + ntb_epc->msix_layout.table_bar = barno; + ntb_epc->msix_layout.table_offset = ctrl_size; + ntb_epc->msix_layout.table_size = msix_table_size; + ntb_epc->msix_layout.pba_bar = barno; + ntb_epc->msix_layout.pba_offset = ctrl_size + msix_table_size; + ntb_epc->msix_layout.pba_size = pba_size; ctrl_size = ctrl_size + msix_table_size + pba_size; } @@ -1317,10 +1320,9 @@ static int epf_ntb_configure_interrupt(struct epf_ntb *ntb, if (msix_capable) { ret = pci_epc_set_msix(epc, func_no, vfunc_no, ntb->db_count, - ntb_epc->msix_bar, - ntb_epc->msix_table_offset); + &ntb_epc->msix_layout); if (ret) { - dev_err(dev, "MSI configuration failed\n"); + dev_err(dev, "MSI-X configuration failed\n"); return ret; } } diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index 4802d4f80f78..e48292e0fb37 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -82,7 +82,7 @@ struct pci_epf_test { struct pci_epf *epf; struct config_group group; enum pci_barno test_reg_bar; - size_t msix_table_offset; + struct pci_epc_msix_layout msix_layout; struct delayed_work cmd_handler; struct dma_chan *dma_chan_tx; struct dma_chan *dma_chan_rx; @@ -1221,8 +1221,7 @@ static int pci_epf_test_epc_init(struct pci_epf *epf) if (epc_features->msix_capable) { ret = pci_epc_set_msix(epc, epf->func_no, epf->vfunc_no, epf->msix_interrupts, - epf_test->test_reg_bar, - epf_test->msix_table_offset); + &epf_test->msix_layout); if (ret) { dev_err(dev, "MSI-X configuration failed\n"); return ret; @@ -1288,9 +1287,15 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) if (epc_features->msix_capable) { msix_table_size = PCI_MSIX_ENTRY_SIZE * epf->msix_interrupts; - epf_test->msix_table_offset = test_reg_bar_size; - /* Align to QWORD or 8 Bytes */ - pba_size = ALIGN(DIV_ROUND_UP(epf->msix_interrupts, 8), 8); + pba_size = BITS_TO_U64(epf->msix_interrupts) * sizeof(u64); + + epf_test->msix_layout.table_bar = test_reg_bar; + epf_test->msix_layout.table_offset = test_reg_bar_size; + epf_test->msix_layout.table_size = msix_table_size; + epf_test->msix_layout.pba_bar = test_reg_bar; + epf_test->msix_layout.pba_offset = test_reg_bar_size + + msix_table_size; + epf_test->msix_layout.pba_size = pba_size; } test_reg_size = test_reg_bar_size + msix_table_size + pba_size; diff --git a/drivers/pci/endpoint/pci-epc-core.c b/drivers/pci/endpoint/pci-epc-core.c index 831b40458dcd..4a0ceb62f38f 100644 --- a/drivers/pci/endpoint/pci-epc-core.c +++ b/drivers/pci/endpoint/pci-epc-core.c @@ -6,6 +6,7 @@ * Author: Kishon Vijay Abraham I <[email protected]> */ +#include <linux/bitops.h> #include <linux/device.h> #include <linux/slab.h> #include <linux/module.h> @@ -113,6 +114,84 @@ enum pci_barno pci_epc_get_next_free_bar(const struct pci_epc_features } EXPORT_SYMBOL_GPL(pci_epc_get_next_free_bar); +static int pci_epc_get_msix_region(const struct pci_epc_features *epc_features, + enum pci_epc_bar_rsvd_region_type type, + enum pci_barno *bar, u32 *offset, + resource_size_t *size) +{ + const struct pci_epc_bar_rsvd_region *region; + const struct pci_epc_bar_desc *bar_desc; + bool found = false; + int i, j; + + if (!epc_features) + return -ENOENT; + + for (i = BAR_0; i < PCI_STD_NUM_BARS; i++) { + bar_desc = &epc_features->bar[i]; + if (bar_desc->nr_rsvd_regions && !bar_desc->rsvd_regions) + return -EINVAL; + + for (j = 0; j < bar_desc->nr_rsvd_regions; j++) { + region = &bar_desc->rsvd_regions[j]; + if (region->type != type) + continue; + + if (found || bar_desc->type != BAR_RESERVED || !region->size || + region->offset > PCI_MSIX_TABLE_OFFSET || + !IS_ALIGNED(region->offset, 8)) + return -EINVAL; + + found = true; + *bar = i; + *offset = region->offset; + *size = region->size; + } + } + + return found ? 0 : -ENOENT; +} + +/** + * pci_epc_get_hw_msix_layout() - get a hardware-owned MSI-X table and PBA layout + * @epc_features: features provided by an EPC for an endpoint function + * @layout: layout to populate + * + * Return: 0 if the EPC describes both hardware-owned MSI-X regions, -ENOENT if + * neither region is described, or an error if the description is invalid. + */ +int pci_epc_get_hw_msix_layout(const struct pci_epc_features *epc_features, + struct pci_epc_msix_layout *layout) +{ + struct pci_epc_msix_layout hw_layout; + int table_ret, pba_ret; + + if (!layout) + return -EINVAL; + + table_ret = pci_epc_get_msix_region(epc_features, + PCI_EPC_BAR_RSVD_MSIX_TBL_RAM, + &hw_layout.table_bar, + &hw_layout.table_offset, + &hw_layout.table_size); + pba_ret = pci_epc_get_msix_region(epc_features, + PCI_EPC_BAR_RSVD_MSIX_PBA_RAM, + &hw_layout.pba_bar, + &hw_layout.pba_offset, + &hw_layout.pba_size); + + if (table_ret == -ENOENT && pba_ret == -ENOENT) + return -ENOENT; + + if (table_ret || pba_ret) + return -EINVAL; + + *layout = hw_layout; + + return 0; +} +EXPORT_SYMBOL_GPL(pci_epc_get_hw_msix_layout); + static bool pci_epc_function_is_valid(struct pci_epc *epc, u8 func_no, u8 vfunc_no) { @@ -443,14 +522,14 @@ EXPORT_SYMBOL_GPL(pci_epc_get_msix); * @func_no: the physical endpoint function number in the EPC device * @vfunc_no: the virtual endpoint function number in the physical function * @nr_irqs: number of MSI-X interrupts required by the EPF - * @bir: BAR where the MSI-X table resides - * @offset: Offset pointing to the start of MSI-X table + * @layout: MSI-X table and PBA layout selected by the EPF * * Invoke to set the required number of MSI-X interrupts. */ int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no, u16 nr_irqs, - enum pci_barno bir, u32 offset) + const struct pci_epc_msix_layout *layout) { + size_t table_size, pba_size; int ret; if (!pci_epc_function_is_valid(epc, func_no, vfunc_no)) @@ -459,11 +538,26 @@ int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no, u16 nr_irqs, if (nr_irqs < 1 || nr_irqs > 2048) return -EINVAL; + if (!layout || layout->table_bar < BAR_0 || + layout->table_bar >= PCI_STD_NUM_BARS || + layout->pba_bar < BAR_0 || layout->pba_bar >= PCI_STD_NUM_BARS || + !IS_ALIGNED(layout->table_offset, 8) || + !IS_ALIGNED(layout->pba_offset, 8) || + layout->table_offset > PCI_MSIX_TABLE_OFFSET || + layout->pba_offset > PCI_MSIX_PBA_OFFSET) + return -EINVAL; + + table_size = nr_irqs * PCI_MSIX_ENTRY_SIZE; + pba_size = BITS_TO_U64(nr_irqs) * sizeof(u64); + + if (layout->table_size < table_size || layout->pba_size < pba_size) + return -ENOSPC; + if (!epc->ops->set_msix) return 0; mutex_lock(&epc->lock); - ret = epc->ops->set_msix(epc, func_no, vfunc_no, nr_irqs, bir, offset); + ret = epc->ops->set_msix(epc, func_no, vfunc_no, nr_irqs, layout); mutex_unlock(&epc->lock); return ret; diff --git a/include/linux/pci-epc.h b/include/linux/pci-epc.h index f247cf9bcf1a..3d28231f092a 100644 --- a/include/linux/pci-epc.h +++ b/include/linux/pci-epc.h @@ -102,6 +102,24 @@ struct pci_epc_aux_resource { } u; }; +/** + * struct pci_epc_msix_layout - layout of an MSI-X table and PBA + * @table_bar: BAR containing the MSI-X table + * @table_offset: offset of the MSI-X table within @table_bar + * @table_size: size of the MSI-X table region + * @pba_bar: BAR containing the MSI-X Pending Bit Array (PBA) + * @pba_offset: offset of the MSI-X PBA within @pba_bar + * @pba_size: size of the MSI-X PBA region + */ +struct pci_epc_msix_layout { + enum pci_barno table_bar; + u32 table_offset; + resource_size_t table_size; + enum pci_barno pba_bar; + u32 pba_offset; + resource_size_t pba_size; +}; + /** * struct pci_epc_ops - set of function pointers for performing EPC operations * @write_header: ops to populate configuration space header @@ -147,7 +165,8 @@ struct pci_epc_ops { u8 nr_irqs); int (*get_msi)(struct pci_epc *epc, u8 func_no, u8 vfunc_no); int (*set_msix)(struct pci_epc *epc, u8 func_no, u8 vfunc_no, - u16 nr_irqs, enum pci_barno, u32 offset); + u16 nr_irqs, + const struct pci_epc_msix_layout *layout); int (*get_msix)(struct pci_epc *epc, u8 func_no, u8 vfunc_no); int (*raise_irq)(struct pci_epc *epc, u8 func_no, u8 vfunc_no, unsigned int type, u16 interrupt_num); @@ -381,8 +400,10 @@ void pci_epc_unmap_addr(struct pci_epc *epc, u8 func_no, u8 vfunc_no, int pci_epc_set_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no, u8 nr_irqs); int pci_epc_get_msi(struct pci_epc *epc, u8 func_no, u8 vfunc_no); int pci_epc_set_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no, u16 nr_irqs, - enum pci_barno, u32 offset); + const struct pci_epc_msix_layout *layout); int pci_epc_get_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no); +int pci_epc_get_hw_msix_layout(const struct pci_epc_features *epc_features, + struct pci_epc_msix_layout *layout); int pci_epc_map_msi_irq(struct pci_epc *epc, u8 func_no, u8 vfunc_no, phys_addr_t phys_addr, u8 interrupt_num, u32 entry_size, u32 *msi_data, u32 *msi_addr_offset); -- 2.51.0