mirror of https://lore.kernel.org/lkml/
 help / color / mirror / Atom feed
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
>

  reply	other threads:[~2026-10-20 17:00 UTC|newest]

Thread overview: 12+ 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-20  5:49 ` [PATCH v6 8/8] Documentation: PCI: endpoint: Document vNTB DMA export Koichiro Den

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 a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox

all inboxes | Powered by JetHome®