From: Frank Li <Frank.li@oss.nxp.com>
To: Koichiro Den <den@valinux.co.jp>
Cc: "Frank Li" <Frank.Li@kernel.org>,
"Manivannan Sadhasivam" <mani@kernel.org>,
"Krzysztof Wilczyński" <kwilczynski@kernel.org>,
"Kishon Vijay Abraham I" <kishon@kernel.org>,
"Jon Mason" <jdmason@kudzu.us>,
"Dave Jiang" <dave.jiang@intel.com>,
"Allen Hubbe" <allenbh@gmail.com>,
"Niklas Cassel" <cassel@kernel.org>,
"Bjorn Helgaas" <bhelgaas@google.com>,
"Jonathan Corbet" <corbet@lwn.net>,
"Shuah Khan" <skhan@linuxfoundation.org>,
"Randy Dunlap" <rdunlap@infradead.org>,
"Jingoo Han" <jingoohan1@gmail.com>,
"Lorenzo Pieralisi" <lpieralisi@kernel.org>,
"Rob Herring" <robh@kernel.org>,
"Jerome Brunet" <jbrunet@baylibre.com>,
linux-pci@vger.kernel.org, linux-doc@vger.kernel.org,
linux-kernel@vger.kernel.org, ntb@lists.linux.dev
Subject: Re: [PATCH v6 5/8] PCI: endpoint: pci-epf-vntb: Export endpoint DMA channels
Date: Tue, 20 Oct 2026 12:00:37 -0500 [thread overview]
Message-ID: <ateeNWaYttaCRN4g@SMW015318> (raw)
In-Reply-To: <20261020054948.1218370-6-den@valinux.co.jp>
On Tue, Oct 20, 2026 at 02:49:45PM +0900, Koichiro Den wrote:
> An RC may use endpoint-local DMA read channels to transfer data directly
> to an endpoint DMA address once both sides agree to use them. Quiescing
> an unrolled eDMA channel disables its whole direction, so reserve the
> complete read direction and route its interrupts to the RC when dma_bar
> is configured.
>
> Describe the controller and per-channel descriptor memory in a private
> control-region extension.
>
> Add a dma_bar configfs attribute. An explicit BAR selection enables DMA
> export. Leaving it unassigned keeps the feature disabled.
>
> Keep resources already assigned to a BAR in place, and map the rest
> through the selected dma_bar.
>
> Signed-off-by: Koichiro Den <den@valinux.co.jp>
> ---
Reviewed-by: Frank Li <Frank.Li@nxp.com>
> Changes since v5:
> - Move the DMA extension layout to include/linux/pci-epf-ntb.h and
> place it right after struct epf_ntb_ctrl instead of embedding it,
> since pci-epf-ntb now shares the structure. (Frank)
> - Small refactorings:
> * Minimize the kernel-doc change in epf_ntb_config_spad_bar_alloc().
> * Group DMA BAR init and clear with the common DMA helpers.
> - Link to v5: https://lore.kernel.org/r/20261009045949.1109871-5-den@valinux.co.jp/
>
> drivers/pci/endpoint/functions/pci-epf-vntb.c | 487 +++++++++++++++++-
> include/linux/pci-epf-ntb.h | 39 ++
> 2 files changed, 517 insertions(+), 9 deletions(-)
>
> diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
> index 3adbaa9d85db..ac83af54122f 100644
> --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c
> +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c
> @@ -39,9 +39,13 @@
> #include <linux/atomic.h>
> #include <linux/bitops.h>
> #include <linux/delay.h>
> +#include <linux/dma/edma.h>
> +#include <linux/dma-mapping.h>
> +#include <linux/dmaengine.h>
> #include <linux/io.h>
> #include <linux/module.h>
> #include <linux/mutex.h>
> +#include <linux/overflow.h>
> #include <linux/slab.h>
>
> #include <linux/pci-ep-msi.h>
> @@ -67,6 +71,7 @@ enum epf_ntb_bar {
> BAR_MW2,
> BAR_MW3,
> BAR_MW4,
> + BAR_DMA,
> VNTB_BAR_NUM,
> };
>
> @@ -78,6 +83,18 @@ enum epf_irq_slot {
>
> #define MIN_DB_COUNT (EPF_IRQ_DB_START + 1)
>
> +struct epf_ntb_dma {
> + struct epf_ntb_dma_ctrl ctrl;
> + struct dma_chan *dchan[EDMA_MAX_RD_CH];
> + void *bar_scratch;
> + dma_addr_t bar_scratch_phys;
> + size_t bar_scratch_size;
> + struct pci_epf_bar_submap submap[EDMA_MAX_RD_CH + 2];
> + struct pci_epf_bar_submap *reg_submap;
> + unsigned int num_submap;
> + u16 rd_ch_cnt;
> +};
> +
> struct epf_ntb {
> struct ntb_dev *ntb;
> struct pci_epf *epf;
> @@ -111,6 +128,7 @@ struct epf_ntb {
> enum pci_barno epf_ntb_bar[VNTB_BAR_NUM];
>
> struct epf_ntb_ctrl *reg;
> + struct epf_ntb_dma *dma;
>
> u32 *epf_db;
>
> @@ -202,7 +220,8 @@ static bool epf_ntb_is_bar_used(struct epf_ntb *ntb,
> {
> int i;
>
> - for (i = 0; i < VNTB_BAR_NUM; i++) {
> + /* BAR_DMA is checked separately because it may share an MW BAR. */
> + for (i = 0; i < BAR_DMA; i++) {
> if (ntb->epf_ntb_bar[i] == barno)
> return true;
> }
> @@ -210,6 +229,419 @@ static bool epf_ntb_is_bar_used(struct epf_ntb *ntb,
> return false;
> }
>
> +struct epf_ntb_dma_filter {
> + struct device *dev;
> + int chan_id;
> +};
> +
> +static bool epf_ntb_dma_filter(struct dma_chan *chan, void *data)
> +{
> + struct epf_ntb_dma_filter *filter = data;
> +
> + return chan->device->dev == filter->dev &&
> + chan->chan_id == filter->chan_id;
> +}
> +
> +static int epf_ntb_dma_add_region(struct epf_ntb_dma *dma,
> + const struct pci_epc_aux_resource *resource,
> + dma_addr_t target_addr,
> + enum pci_barno barno, size_t align, u32 *next,
> + struct epf_ntb_dma_region_ctrl *region)
> +{
> + resource_size_t delta, map_size, size;
> + struct pci_epf_bar_submap *submap;
> + dma_addr_t base;
> +
> + if (!resource->size || resource->size > U32_MAX)
> + return -EINVAL;
> +
> + region->size = resource->size;
> + if (resource->bar != NO_BAR) {
> + if (resource->bar < BAR_0 || resource->bar > BAR_5 ||
> + resource->bar_offset > U32_MAX)
> + return -EINVAL;
> +
> + region->bar = resource->bar;
> + region->offset = resource->bar_offset;
> + return 0;
> + }
> + submap = &dma->submap[dma->num_submap];
> +
> + /*
> + * The RC is trusted with endpoint-local DMA. Map the aligned span
> + * required by the EPC, but advertise only the resource itself.
> + */
> + base = ALIGN_DOWN(target_addr, align);
> + delta = target_addr - base;
> + if (check_add_overflow(delta, resource->size, &size))
> + return -EOVERFLOW;
> + map_size = ALIGN(size, align);
> + if (map_size < size || map_size > U32_MAX - *next)
> + return -EOVERFLOW;
> +
> + submap->phys_addr = base;
> + submap->size = map_size;
> + region->bar = barno;
> + region->offset = *next + delta;
> + *next += map_size;
> + dma->num_submap++;
> +
> + return 0;
> +}
> +
> +/* DW eDMA */
> +
> +static int epf_ntb_dw_edma_claim(struct device *dev, int chan_id,
> + struct dma_chan **dchan)
> +{
> + struct epf_ntb_dma_filter filter = {
> + .dev = dev,
> + .chan_id = chan_id,
> + };
> + dma_cap_mask_t mask;
> +
> + dma_cap_zero(mask);
> + dma_cap_set(DMA_SLAVE, mask);
> + *dchan = dma_request_channel(mask, epf_ntb_dma_filter, &filter);
> +
> + return *dchan ? 0 : -EBUSY;
> +}
> +
> +static void epf_ntb_dw_edma_release_channels(struct epf_ntb_dma *dma)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < dma->rd_ch_cnt; i++) {
> + if (!dma->dchan[i])
> + continue;
> +
> + dma_release_channel(dma->dchan[i]);
> + }
> +}
> +
> +static const struct pci_epc_aux_resource *
> +epf_ntb_dw_edma_find_desc(const struct pci_epc_aux_resource *resources,
> + unsigned int count, u16 chan_id)
> +{
> + unsigned int i;
> +
> + for (i = 0; i < count; i++)
> + if (resources[i].type == PCI_EPC_AUX_DMA_DESC_MEM &&
> + resources[i].u.dma_desc.chan_id == chan_id)
> + return &resources[i];
> +
> + return NULL;
> +}
> +
> +static int epf_ntb_dw_edma_collect(struct epf_ntb *ntb,
> + struct epf_ntb_dma *dma,
> + const struct pci_epc_aux_resource *ctrl,
> + const struct pci_epc_aux_resource *resources,
> + unsigned int count)
> +{
> + struct dw_edma_chan_config chan_config = {
> + .flags = DW_EDMA_CH_CONFIG_IRQ_MODE,
> + .irq_mode = DW_EDMA_CH_IRQ_REMOTE,
> + };
> + struct dma_slave_config config = {
> + .peripheral_config = &chan_config,
> + .peripheral_size = sizeof(chan_config),
> + };
> + enum pci_barno barno = ntb->epf_ntb_bar[BAR_DMA];
> + const struct pci_epc_features *features;
> + struct device *dma_dev;
> + unsigned int i;
> + size_t align;
> + u32 next = 0;
> + int ret;
> +
> + if (ctrl->u.dma_ctrl.reg_layout_data != EDMA_MF_EDMA_UNROLL)
> + return -EOPNOTSUPP;
> +
> + dma->rd_ch_cnt = ctrl->u.dma_ctrl.rc_to_ep_ch_cnt;
> +
> + features = pci_epc_get_features(ntb->epf->epc, ntb->epf->func_no,
> + ntb->epf->vfunc_no);
> + /* DWC descriptor memory always needs subrange mappings. */
> + if (!features || !features->subrange_mapping ||
> + !features->dynamic_inbound_mapping)
> + return -EOPNOTSUPP;
> +
> + align = features->align ?: 1;
> + if (!is_power_of_2(align))
> + return -EINVAL;
> +
> + if (epf_ntb_is_bar_used(ntb, barno) ||
> + pci_epc_get_next_free_bar(features, barno) != barno)
> + return -EINVAL;
> +
> + dma->ctrl.magic = EPF_NTB_DMA_MAGIC;
> + dma->ctrl.revision = EPF_NTB_DMA_REVISION;
> + dma->ctrl.type = EPF_NTB_DMA_TYPE_DW_EDMA;
> + dma->ctrl.submap.bar = barno;
> + dma->ctrl.length = offsetof(struct epf_ntb_dma_ctrl,
> + chan[dma->rd_ch_cnt]);
> +
> + if (ctrl->bar == NO_BAR)
> + dma->reg_submap = &dma->submap[dma->num_submap];
> + ret = epf_ntb_dma_add_region(dma, ctrl, ctrl->phys_addr,
> + barno, align, &next, &dma->ctrl.reg);
> + if (ret)
> + return ret;
> +
> + /* DW eDMA static IDs place read channels after all write channels. */
> + for (i = 0; i < dma->rd_ch_cnt; i++) {
> + u16 chan_id = ctrl->u.dma_ctrl.ep_to_rc_ch_cnt + i;
> + struct epf_ntb_dma_chan_ctrl *chan = &dma->ctrl.chan[i];
> + const struct pci_epc_aux_resource *desc;
> + dma_addr_t dma_addr;
> +
> + desc = epf_ntb_dw_edma_find_desc(resources, count, chan_id);
> + if (!desc)
> + return -EINVAL;
> + dma_addr = desc->u.dma_desc.dma_addr;
> +
> + ret = epf_ntb_dma_add_region(dma, desc, dma_addr, barno,
> + align, &next, &chan->desc);
> + if (ret)
> + return ret;
> + chan->desc_addr_lo = lower_32_bits(dma_addr);
> + chan->desc_addr_hi = upper_32_bits(dma_addr);
> + }
> + dma->ctrl.submap.size = next;
> +
> + dma_dev = ntb->epf->epc->dev.parent;
> + for (i = 0; i < dma->rd_ch_cnt; i++) {
> + u16 chan_id = ctrl->u.dma_ctrl.ep_to_rc_ch_cnt + i;
> +
> + ret = epf_ntb_dw_edma_claim(dma_dev, chan_id, &dma->dchan[i]);
> + if (ret)
> + return ret;
> + }
> +
> + for (i = 0; i < dma->rd_ch_cnt; i++) {
> + ret = dmaengine_slave_config(dma->dchan[i], &config);
> + if (ret)
> + return ret;
> + }
> +
> + return 0;
> +}
> +
> +/* Common endpoint DMA */
> +
> +static void epf_ntb_dma_release_channels(struct epf_ntb_dma *dma)
> +{
> + switch (dma->ctrl.type) {
> + case EPF_NTB_DMA_TYPE_DW_EDMA:
> + epf_ntb_dw_edma_release_channels(dma);
> + break;
> + }
> +}
> +
> +static int epf_ntb_dma_collect(struct epf_ntb *ntb)
> +{
> + const struct pci_epc_aux_resource *ctrl = NULL;
> + struct device *dma_dev;
> + dma_addr_t dma_addr;
> + unsigned int i;
> + int count, ret;
> +
> + if (ntb->epf_ntb_bar[BAR_DMA] == NO_BAR)
> + return 0;
> +
> + count = pci_epc_get_aux_resources_count(ntb->epf->epc,
> + ntb->epf->func_no,
> + ntb->epf->vfunc_no);
> + if (count <= 0)
> + return count ?: -ENODEV;
> +
> + struct pci_epc_aux_resource *resources __free(kfree) =
> + kcalloc(count, sizeof(*resources), GFP_KERNEL);
> + if (!resources)
> + return -ENOMEM;
> +
> + ret = pci_epc_get_aux_resources(ntb->epf->epc, ntb->epf->func_no,
> + ntb->epf->vfunc_no, resources, count);
> + if (ret)
> + return ret;
> +
> + for (i = 0; i < count; i++) {
> + if (resources[i].type != PCI_EPC_AUX_DMA_CTRL_MMIO)
> + continue;
> + if (ctrl)
> + return -EINVAL;
> + ctrl = &resources[i];
> + }
> + if (!ctrl)
> + return -ENODEV;
> +
> + struct epf_ntb_dma *dma __free(kfree) =
> + kzalloc(sizeof(*dma), GFP_KERNEL);
> + if (!dma)
> + return -ENOMEM;
> +
> + switch (ctrl->u.dma_ctrl.reg_layout) {
> + case PCI_EPC_AUX_DMA_REG_LAYOUT_DW_EDMA:
> + ret = epf_ntb_dw_edma_collect(ntb, dma, ctrl, resources, count);
> + break;
> + default:
> + return -EOPNOTSUPP;
> + }
> + if (ret) {
> + epf_ntb_dma_release_channels(dma);
> + return ret;
> + }
> +
> + /*
> + * CTRL_MMIO carries a CPU physical address, while DMA_DESC_MEM already
> + * carries an endpoint DMA address. Convert only the former.
> + */
> + if (dma->reg_submap) {
> + dma_dev = ntb->epf->epc->dev.parent;
> + dma_addr = dma_map_resource(dma_dev, dma->reg_submap->phys_addr,
> + dma->reg_submap->size,
> + DMA_BIDIRECTIONAL, 0);
> + if (dma_mapping_error(dma_dev, dma_addr)) {
> + epf_ntb_dma_release_channels(dma);
> + return -EIO;
> + }
> + dma->reg_submap->phys_addr = dma_addr;
> + }
> +
> + ntb->dma = no_free_ptr(dma);
> +
> + return 0;
> +}
> +
> +static void epf_ntb_dma_release(struct epf_ntb *ntb)
> +{
> + struct epf_ntb_dma *dma = ntb->dma;
> + struct device *dev;
> +
> + if (!dma)
> + return;
> +
> + epf_ntb_dma_release_channels(dma);
> + dev = ntb->epf->epc->dev.parent;
> + if (dma->reg_submap)
> + dma_unmap_resource(dev, dma->reg_submap->phys_addr,
> + dma->reg_submap->size, DMA_BIDIRECTIONAL, 0);
> + if (dma->bar_scratch)
> + dma_free_coherent(dev, dma->bar_scratch_size,
> + dma->bar_scratch, dma->bar_scratch_phys);
> + kfree(dma);
> + ntb->dma = NULL;
> +}
> +
> +static int epf_ntb_dma_bar_init(struct epf_ntb *ntb)
> +{
> + struct device *dev = ntb->epf->epc->dev.parent;
> + const struct pci_epc_features *features;
> + struct epf_ntb_dma *dma = ntb->dma;
> + struct pci_epf_bar *bar;
> + enum pci_barno barno;
> + size_t backing_size;
> + u32 mapped_size;
> + int ret;
> +
> + features = pci_epc_get_features(ntb->epf->epc, ntb->epf->func_no,
> + ntb->epf->vfunc_no);
> + if (!features)
> + return -EOPNOTSUPP;
> +
> + barno = ntb->epf_ntb_bar[BAR_DMA];
> + mapped_size = dma->ctrl.submap.size;
> + /*
> + * Submaps cannot be installed until the host assigns the BAR address.
> + * Use address 0 for the temporary BAR Match Mode mapping, as is done
> + * for regular vNTB MW BARs.
> + */
> + ret = pci_epf_assign_bar_space(ntb->epf, mapped_size, barno, features,
> + PRIMARY_INTERFACE, 0);
> + if (ret)
> + return ret;
> +
> + bar = &ntb->epf->bar[barno];
> + if (bar->size > U32_MAX)
> + return -EOVERFLOW;
> +
> + backing_size = bar->size - mapped_size;
> + if (backing_size) {
> + /* Back the BAR tail added by the power-of-two size rounding. */
> + dma->bar_scratch = dma_alloc_coherent(dev, backing_size,
> + &dma->bar_scratch_phys,
> + GFP_KERNEL);
> + if (!dma->bar_scratch)
> + return -ENOMEM;
> + dma->bar_scratch_size = backing_size;
> + if (!IS_ALIGNED(dma->bar_scratch_phys, features->align ?: 1))
> + return -EINVAL;
> +
> + dma->submap[dma->num_submap++] = (struct pci_epf_bar_submap) {
> + .phys_addr = dma->bar_scratch_phys,
> + .size = backing_size,
> + };
> + }
> +
> + return pci_epc_set_bar(ntb->epf->epc, ntb->epf->func_no,
> + ntb->epf->vfunc_no, bar);
> +}
> +
> +static void epf_ntb_dma_bar_clear(struct epf_ntb *ntb)
> +{
> + struct epf_ntb_dma *dma = ntb->dma;
> + struct pci_epf_bar *bar;
> + enum pci_barno barno;
> +
> + if (!dma)
> + return;
> +
> + barno = ntb->epf_ntb_bar[BAR_DMA];
> + bar = &ntb->epf->bar[barno];
> + if (!bar->size)
> + return;
> +
> + pci_epc_clear_bar(ntb->epf->epc, ntb->epf->func_no,
> + ntb->epf->vfunc_no, bar);
> + memset(bar, 0, sizeof(*bar));
> +}
> +
> +static int epf_ntb_dma_set_bar(struct epf_ntb *ntb)
> +{
> + struct pci_epf_bar_submap *old_submap;
> + struct epf_ntb_dma *dma = ntb->dma;
> + unsigned int old_num_submap;
> + struct pci_epf_bar *bar;
> + int restore, ret;
> +
> + if (!dma)
> + return 0;
> +
> + bar = &ntb->epf->bar[ntb->epf_ntb_bar[BAR_DMA]];
> +
> + old_submap = bar->submap;
> + old_num_submap = bar->num_submap;
> + bar->submap = dma->submap;
> + bar->num_submap = dma->num_submap;
> +
> + ret = pci_epc_set_bar(ntb->epf->epc, ntb->epf->func_no,
> + ntb->epf->vfunc_no, bar);
> + if (!ret)
> + return 0;
> +
> + /* A failed dynamic update may have already removed the old mapping. */
> + bar->submap = old_submap;
> + bar->num_submap = old_num_submap;
> + restore = pci_epc_set_bar(ntb->epf->epc, ntb->epf->func_no,
> + ntb->epf->vfunc_no, bar);
> + if (restore)
> + dev_warn(&ntb->epf->dev,
> + "failed to restore DMA BAR mapping: %d\n", restore);
> +
> + return ret;
> +}
> +
> /**
> * epf_ntb_configure_mw() - Configure the Outbound Address Space for VHOST
> * to access the memory window of HOST
> @@ -342,6 +774,11 @@ static void epf_ntb_cmd_handler(struct work_struct *work)
> epf_ntb_teardown_mw(ntb, argument);
> ctrl->command_status = EPF_NTB_COMMAND_STATUS_OK;
> break;
> + case EPF_NTB_COMMAND_SETUP_DMA_BAR:
> + ret = epf_ntb_dma_set_bar(ntb);
> + ctrl->command_status = ret ? EPF_NTB_COMMAND_STATUS_ERROR :
> + EPF_NTB_COMMAND_STATUS_OK;
> + break;
> case EPF_NTB_COMMAND_LINK_UP:
> ret = epf_ntb_set_link_enabled(ntb, true);
> if (ret < 0)
> @@ -488,16 +925,17 @@ static void epf_ntb_config_spad_bar_free(struct epf_ntb *ntb)
> * Virtual PCI PCIe Endpoint
> * NTB Driver NTB Driver
> *
> - * Allocate the Local Memory mentioned in the above diagram. The size of
> - * 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.
> + * Allocate the Local Memory mentioned in the above diagram. CONFIG REGION
> + * contains struct epf_ntb_ctrl, the DMA extension if enabled, and the MSI-X
> + * Table and PBA if supported, in that order. The size of SCRATCHPAD REGION
> + * is obtained from "spad-count" configfs entry.
> *
> * Returns: Zero for success, or an error code in case of failure
> */
> static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb)
> {
> enum pci_barno barno;
> + struct epf_ntb_dma_ctrl *dma_ctrl;
> struct epf_ntb_ctrl *ctrl;
> u32 spad_size, ctrl_size;
> struct pci_epf *epf = ntb->epf;
> @@ -511,7 +949,10 @@ static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb)
> barno = ntb->epf_ntb_bar[BAR_CONFIG];
> spad_count = ntb->spad_count;
>
> - ctrl_size = ALIGN(sizeof(struct epf_ntb_ctrl), sizeof(u32));
> + ctrl_size = sizeof(struct epf_ntb_ctrl);
> + if (ntb->dma)
> + ctrl_size += sizeof(struct epf_ntb_dma_ctrl);
> + ctrl_size = ALIGN(ctrl_size, sizeof(u32));
> if (epc_features->msix_capable) {
> ntb->msix_table_offset = ALIGN(ctrl_size, 8);
> ctrl_size = ntb->msix_table_offset +
> @@ -543,6 +984,11 @@ static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb)
> ntb->reg->db_offset[i] = 0;
> }
>
> + if (ntb->dma) {
> + dma_ctrl = base + EPF_NTB_DMA_CTRL_OFFSET;
> + *dma_ctrl = ntb->dma->ctrl;
> + }
> +
> return 0;
> }
>
> @@ -924,7 +1370,8 @@ static int epf_ntb_find_bar(struct epf_ntb *ntb,
> * Verify if the BAR found is not already assigned
> * through the provided configuration
> */
> - if (!epf_ntb_is_bar_used(ntb, barno))
> + if (ntb->epf_ntb_bar[BAR_DMA] != barno &&
> + !epf_ntb_is_bar_used(ntb, barno))
> ntb->epf_ntb_bar[bar] = barno;
>
> barno += 1;
> @@ -1023,11 +1470,19 @@ static int epf_ntb_epc_init(struct epf_ntb *ntb)
> goto err_mw_bar_init;
> }
>
> + if (ntb->dma) {
> + ret = epf_ntb_dma_bar_init(ntb);
> + if (ret) {
> + dev_err(dev, "DMA BAR init failed\n");
> + goto err_dma_bar_init;
> + }
> + }
> +
> if (vfunc_no <= 1) {
> ret = pci_epc_write_header(epc, func_no, vfunc_no, epf->header);
> if (ret) {
> dev_err(dev, "Configuration header write failed\n");
> - goto err_write_header;
> + goto err_dma_bar_init;
> }
> }
>
> @@ -1038,7 +1493,8 @@ static int epf_ntb_epc_init(struct epf_ntb *ntb)
>
> return 0;
>
> -err_write_header:
> +err_dma_bar_init:
> + epf_ntb_dma_bar_clear(ntb);
> epf_ntb_mw_bar_clear(ntb, ntb->num_mws);
> err_mw_bar_init:
> epf_ntb_db_bar_clear(ntb);
> @@ -1061,6 +1517,7 @@ static void epf_ntb_epc_cleanup(struct epf_ntb *ntb)
> disable_delayed_work_sync(&ntb->cmd_handler);
> disable_work_sync(&ntb->peer_db_work);
> atomic64_set(&ntb->peer_db_pending, 0);
> + epf_ntb_dma_bar_clear(ntb);
> epf_ntb_mw_bar_clear(ntb, ntb->num_mws);
> epf_ntb_db_bar_clear(ntb);
> epf_ntb_config_sspad_bar_clear(ntb);
> @@ -1267,10 +1724,13 @@ EPF_NTB_BAR_R(mw3_bar, BAR_MW3)
> EPF_NTB_BAR_W(mw3_bar, BAR_MW3)
> EPF_NTB_BAR_R(mw4_bar, BAR_MW4)
> EPF_NTB_BAR_W(mw4_bar, BAR_MW4)
> +EPF_NTB_BAR_R(dma_bar, BAR_DMA)
> +EPF_NTB_BAR_W(dma_bar, BAR_DMA)
>
> CONFIGFS_ATTR(epf_ntb_, spad_count);
> CONFIGFS_ATTR(epf_ntb_, db_count);
> CONFIGFS_ATTR(epf_ntb_, num_mws);
> +CONFIGFS_ATTR(epf_ntb_, dma_bar);
> CONFIGFS_ATTR(epf_ntb_, mw1);
> CONFIGFS_ATTR(epf_ntb_, mw2);
> CONFIGFS_ATTR(epf_ntb_, mw3);
> @@ -1289,6 +1749,7 @@ static struct configfs_attribute *epf_ntb_attrs[] = {
> &epf_ntb_attr_spad_count,
> &epf_ntb_attr_db_count,
> &epf_ntb_attr_num_mws,
> + &epf_ntb_attr_dma_bar,
> &epf_ntb_attr_mw1,
> &epf_ntb_attr_mw2,
> &epf_ntb_attr_mw3,
> @@ -1847,6 +2308,12 @@ static int epf_ntb_bind(struct pci_epf *epf)
> return ret;
> }
>
> + ret = epf_ntb_dma_collect(ntb);
> + if (ret) {
> + dev_err(dev, "Failed to prepare NTB DMA export\n");
> + return ret;
> + }
> +
> ret = epf_ntb_config_spad_bar_alloc(ntb);
> if (ret) {
> dev_err(dev, "Failed to allocate BAR memory\n");
> @@ -1887,6 +2354,7 @@ static int epf_ntb_bind(struct pci_epf *epf)
> epf_ntb_epc_cleanup(ntb);
> err_bar_alloc:
> epf_ntb_config_spad_bar_free(ntb);
> + epf_ntb_dma_release(ntb);
>
> return ret;
> }
> @@ -1918,6 +2386,7 @@ static void epf_ntb_unbind(struct pci_epf *epf)
>
> epf_ntb_epc_cleanup(ntb);
> epf_ntb_config_spad_bar_free(ntb);
> + epf_ntb_dma_release(ntb);
> }
>
> // EPF driver probe
> diff --git a/include/linux/pci-epf-ntb.h b/include/linux/pci-epf-ntb.h
> index 0c02d709aa08..5cc0f0badae0 100644
> --- a/include/linux/pci-epf-ntb.h
> +++ b/include/linux/pci-epf-ntb.h
> @@ -8,6 +8,8 @@
> #define __LINUX_PCI_EPF_NTB_H
>
> #include <linux/bits.h>
> +#include <linux/dma/edma.h>
> +#include <linux/stddef.h>
> #include <linux/types.h>
>
> #define EPF_NTB_COMMAND_CONFIGURE_DOORBELL 1
> @@ -16,6 +18,7 @@
> #define EPF_NTB_COMMAND_TEARDOWN_MW 4
> #define EPF_NTB_COMMAND_LINK_UP 5
> #define EPF_NTB_COMMAND_LINK_DOWN 6
> +#define EPF_NTB_COMMAND_SETUP_DMA_BAR 7
>
> /* EPF_NTB_COMMAND_CONFIGURE_DOORBELL argument */
> #define EPF_NTB_DB_COUNT_MASK GENMASK(15, 0)
> @@ -45,4 +48,40 @@ struct epf_ntb_ctrl {
> u32 db_offset[EPF_NTB_MAX_DB_COUNT];
> } __packed;
>
> +/*
> + * DMA wire extension produced by pci-epf-vntb and consumed by
> + * ntb_hw_epf. It immediately follows struct epf_ntb_ctrl.
> + */
> +#define EPF_NTB_DMA_CTRL_OFFSET sizeof(struct epf_ntb_ctrl)
> +
> +#define EPF_NTB_DMA_MAGIC 0x414d444e /* "NDMA": NTB DMA */
> +#define EPF_NTB_DMA_REVISION 1
> +#define EPF_NTB_DMA_TYPE_DW_EDMA 1
> +
> +struct epf_ntb_dma_region_ctrl {
> + u32 bar;
> + u32 offset;
> + u32 size;
> +} __packed;
> +
> +struct epf_ntb_dma_chan_ctrl {
> + struct epf_ntb_dma_region_ctrl desc;
> + u32 desc_addr_lo;
> + u32 desc_addr_hi;
> +} __packed;
> +
> +struct epf_ntb_dma_ctrl {
> + u32 magic;
> + u16 revision;
> + u16 length;
> + u32 type;
> + /* BAR range occupied by resources without a fixed BAR assignment. */
> + struct epf_ntb_dma_region_ctrl submap;
> + struct epf_ntb_dma_region_ctrl reg;
> + struct epf_ntb_dma_chan_ctrl chan[EDMA_MAX_RD_CH];
> +} __packed;
> +
> +/* Common header needed even when endpoint DMA is unavailable. */
> +#define EPF_NTB_DMA_CTRL_COMMON_SIZE offsetof(struct epf_ntb_dma_ctrl, reg)
> +
> #endif /* __LINUX_PCI_EPF_NTB_H */
> --
> 2.54.0
>
next prev parent reply other threads:[~2026-10-20 17:00 UTC|newest]
Thread overview: 14+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-10-20 5:49 [PATCH v6 0/8] PCI: endpoint: Remote DMA support via vNTB Koichiro Den
2026-10-20 5:49 ` [PATCH v6 1/8] PCI: endpoint: Add DMA auxiliary resource metadata Koichiro Den
2026-10-20 5:49 ` [PATCH v6 2/8] PCI: dwc: Expose endpoint DMA resources Koichiro Den
2026-10-20 5:49 ` [PATCH v6 3/8] PCI: endpoint: pci-epf-vntb: Move epf_ntb_is_bar_used() up Koichiro Den
2026-10-20 5:49 ` [PATCH v6 4/8] PCI: endpoint: Add pci-epf-ntb.h for the shared NTB control layout Koichiro Den
2026-10-20 5:49 ` [PATCH v6 5/8] PCI: endpoint: pci-epf-vntb: Export endpoint DMA channels Koichiro Den
2026-10-20 17:00 ` Frank Li [this message]
2026-10-20 5:49 ` [PATCH v6 6/8] NTB: ntb_hw_epf: Discover vNTB-embedded DMA Koichiro Den
2026-10-20 17:03 ` Frank Li
2026-10-20 5:49 ` [PATCH v6 7/8] PCI/NTB: endpoint: Allow DMA and MW to share a BAR Koichiro Den
2026-10-20 17:11 ` Frank Li
2026-10-21 1:39 ` Koichiro Den
2026-10-20 5:49 ` [PATCH v6 8/8] Documentation: PCI: endpoint: Document vNTB DMA export Koichiro Den
2026-10-20 18:49 ` Frank Li
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=ateeNWaYttaCRN4g@SMW015318 \
--to=frank.li@oss.nxp.com \
--cc=Frank.Li@kernel.org \
--cc=allenbh@gmail.com \
--cc=bhelgaas@google.com \
--cc=cassel@kernel.org \
--cc=corbet@lwn.net \
--cc=dave.jiang@intel.com \
--cc=den@valinux.co.jp \
--cc=jbrunet@baylibre.com \
--cc=jdmason@kudzu.us \
--cc=jingoohan1@gmail.com \
--cc=kishon@kernel.org \
--cc=kwilczynski@kernel.org \
--cc=linux-doc@vger.kernel.org \
--cc=linux-kernel@vger.kernel.org \
--cc=linux-pci@vger.kernel.org \
--cc=lpieralisi@kernel.org \
--cc=mani@kernel.org \
--cc=ntb@lists.linux.dev \
--cc=rdunlap@infradead.org \
--cc=robh@kernel.org \
--cc=skhan@linuxfoundation.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.