[PATCH v3 4/7] PCI: endpoint: pci-epf-vntb: Export endpoint DMA channels
From: Koichiro Den
Date: Mon Aug 31 2026 - 18:52:41 EST
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@xxxxxxxxxxxxx>
---
Changes in v3:
- Use dma_bar as the opt-in and drop use_dma and automatic BAR selection.
- Split DMA/MW BAR sharing and the documentation into separate patches.
- Avoid mixing cleanup helpers with goto-based error paths. (Sashiko)
- Use pci_epf_assign_bar_space() and back only uncovered BAR ranges.
(Sashiko)
drivers/pci/endpoint/functions/pci-epf-vntb.c | 575 +++++++++++++++++-
1 file changed, 567 insertions(+), 8 deletions(-)
diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c
index d12d134ce553..1842493c33b0 100644
--- a/drivers/pci/endpoint/functions/pci-epf-vntb.c
+++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c
@@ -39,8 +39,12 @@
#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/overflow.h>
#include <linux/slab.h>
#include <linux/pci-ep-msi.h>
@@ -56,6 +60,8 @@ static struct workqueue_struct *kpcintb_workqueue;
#define COMMAND_TEARDOWN_MW 4
#define COMMAND_LINK_UP 5
#define COMMAND_LINK_DOWN 6
+#define COMMAND_CONFIGURE_DMA 7
+#define COMMAND_TEARDOWN_DMA 8
#define COMMAND_STATUS_OK 1
#define COMMAND_STATUS_ERROR 2
@@ -69,6 +75,10 @@ static struct workqueue_struct *kpcintb_workqueue;
#define MSIX_ENABLE BIT(16)
#define MAX_MW 4
+#define EPF_NTB_DMA_MAGIC 0x414d444e /* "NDMA": NTB DMA */
+#define EPF_NTB_DMA_REVISION 1
+#define EPF_NTB_DMA_TYPE_DW_EDMA 1
+
/* Limit per-work execution to avoid monopolizing kworker on doorbell storms. */
#define VNTB_PEER_DB_WORK_BUDGET 5
@@ -79,6 +89,7 @@ enum epf_ntb_bar {
BAR_MW2,
BAR_MW3,
BAR_MW4,
+ BAR_DMA,
VNTB_BAR_NUM,
};
@@ -91,6 +102,30 @@ enum epf_irq_slot {
#define MIN_DB_COUNT (EPF_IRQ_DB_START + 1)
#define MAX_DB_COUNT 32
+/* Private wire extension consumed by ntb_hw_epf. */
+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;
+
/*
* +--------------------------------------------------+ Base
* | |
@@ -129,8 +164,21 @@ struct epf_ntb_ctrl {
u32 db_entry_size;
u32 db_data[MAX_DB_COUNT];
u32 db_offset[MAX_DB_COUNT];
+ struct epf_ntb_dma_ctrl dma;
} __packed;
+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;
@@ -159,6 +207,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;
@@ -211,7 +260,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;
}
@@ -219,6 +269,404 @@ static bool epf_ntb_is_bar_used(struct epf_ntb *ntb,
return false;
}
+static int epf_ntb_dma_validate_bar(struct epf_ntb *ntb,
+ const struct pci_epc_features *features)
+{
+ enum pci_barno barno = ntb->epf_ntb_bar[BAR_DMA];
+
+ if (epf_ntb_is_bar_used(ntb, barno) ||
+ pci_epc_get_next_free_bar(features, barno) != barno)
+ return -EINVAL;
+
+ return 0;
+}
+
+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)
+{
+ struct pci_epf_bar_submap *submap;
+ resource_size_t delta, map_size, size;
+ 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];
+
+ /*
+ * Meet the EPC alignment requirement by mapping an aligned superset
+ * and advertising the resource after any leading padding.
+ */
+ 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)
+{
+ enum dw_edma_ch_irq_mode mode = DW_EDMA_CH_IRQ_REMOTE;
+ struct epf_ntb_dma_filter filter = {
+ .dev = dev,
+ .chan_id = chan_id,
+ };
+ struct dma_slave_config config = {
+ .peripheral_config = &mode,
+ .peripheral_size = sizeof(mode),
+ };
+ dma_cap_mask_t mask;
+ struct dma_chan *chan;
+ int ret;
+
+ dma_cap_zero(mask);
+ dma_cap_set(DMA_SLAVE, mask);
+ chan = dma_request_channel(mask, epf_ntb_dma_filter, &filter);
+ if (!chan)
+ return -EBUSY;
+
+ ret = dmaengine_slave_config(chan, &config);
+ if (ret) {
+ dma_release_channel(chan);
+ return ret;
+ }
+
+ *dchan = chan;
+
+ return 0;
+}
+
+static void epf_ntb_dw_edma_release_channels(struct epf_ntb *ntb,
+ struct epf_ntb_dma *dma,
+ bool quiesce)
+{
+ unsigned int i;
+ int ret;
+
+ if (quiesce) {
+ /*
+ * RC programming has stopped and this EPF owns the complete read
+ * direction, so one termination quiesces the direction.
+ */
+ ret = dmaengine_terminate_sync(dma->dchan[0]);
+ if (ret)
+ dev_warn(&ntb->epf->dev,
+ "failed to terminate remote DMA: %d\n", ret);
+ }
+
+ 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)
+{
+ const struct pci_epc_features *features;
+ const struct pci_epc_aux_resource *desc[EDMA_MAX_RD_CH];
+ enum pci_barno barno = ntb->epf_ntb_bar[BAR_DMA];
+ struct device *dma_dev;
+ bool needs_submap;
+ 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;
+ if (ctrl->u.dma_ctrl.ep_to_rc_ch_cnt > EDMA_MAX_WR_CH ||
+ !ctrl->u.dma_ctrl.rc_to_ep_ch_cnt ||
+ ctrl->u.dma_ctrl.rc_to_ep_ch_cnt > EDMA_MAX_RD_CH)
+ return -EINVAL;
+
+ 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);
+ if (!features)
+ return -EOPNOTSUPP;
+
+ align = features->align ?: 1;
+ if (!is_power_of_2(align))
+ return -EINVAL;
+
+ needs_submap = ctrl->bar == NO_BAR;
+ /* 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;
+
+ desc[i] = epf_ntb_dw_edma_find_desc(resources, count, chan_id);
+ if (!desc[i])
+ return -EINVAL;
+ needs_submap |= desc[i]->bar == NO_BAR;
+ }
+ if (needs_submap) {
+ if (!features->subrange_mapping ||
+ !features->dynamic_inbound_mapping)
+ return -EOPNOTSUPP;
+ ret = epf_ntb_dma_validate_bar(ntb, features);
+ if (ret)
+ return ret;
+ }
+
+ 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 = U32_MAX;
+ 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;
+ for (i = 0; i < dma->rd_ch_cnt; i++) {
+ struct epf_ntb_dma_chan_ctrl *chan = &dma->ctrl.chan[i];
+ dma_addr_t dma_addr = desc[i]->u.dma_desc.dma_addr;
+
+ ret = epf_ntb_dma_add_region(dma, desc[i], 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);
+ }
+ if (dma->num_submap) {
+ dma->ctrl.submap.bar = barno;
+ 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;
+ }
+
+ return 0;
+}
+
+/* Common endpoint DMA */
+
+static void epf_ntb_dma_release_channels(struct epf_ntb *ntb,
+ struct epf_ntb_dma *dma,
+ bool quiesce)
+{
+ switch (dma->ctrl.type) {
+ case EPF_NTB_DMA_TYPE_DW_EDMA:
+ epf_ntb_dw_edma_release_channels(ntb, dma, quiesce);
+ 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(ntb, dma, false);
+ 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(ntb, dma, false);
+ 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, bool quiesce)
+{
+ struct epf_ntb_dma *dma = ntb->dma;
+ struct device *dev;
+
+ if (!dma)
+ return;
+
+ epf_ntb_dma_release_channels(ntb, dma, quiesce);
+ 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_set_bar(struct epf_ntb *ntb, bool active)
+{
+ struct pci_epf_bar_submap *old_submap;
+ struct epf_ntb_dma *dma = ntb->dma;
+ struct pci_epf_bar *bar;
+ unsigned int old_num_submap;
+ int restore, ret;
+
+ bar = &ntb->epf->bar[ntb->epf_ntb_bar[BAR_DMA]];
+ old_submap = bar->submap;
+ old_num_submap = bar->num_submap;
+ bar->submap = active ? dma->submap : NULL;
+ bar->num_submap = active ? dma->num_submap : 0;
+
+ 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;
+}
+
+static int epf_ntb_dma_set_active(struct epf_ntb *ntb, bool active)
+{
+ struct epf_ntb_dma *dma = ntb->dma;
+ struct pci_epf_bar *bar;
+
+ if (!dma || !dma->num_submap)
+ return 0;
+
+ bar = &ntb->epf->bar[ntb->epf_ntb_bar[BAR_DMA]];
+ if (active == !!bar->num_submap)
+ return 0;
+
+ return epf_ntb_dma_set_bar(ntb, active);
+}
+
/**
* epf_ntb_configure_mw() - Configure the Outbound Address Space for VHOST
* to access the memory window of HOST
@@ -339,6 +787,13 @@ static void epf_ntb_cmd_handler(struct work_struct *work)
epf_ntb_teardown_mw(ntb, argument);
ctrl->command_status = COMMAND_STATUS_OK;
break;
+ case COMMAND_CONFIGURE_DMA:
+ case COMMAND_TEARDOWN_DMA:
+ ret = epf_ntb_dma_set_active(ntb,
+ command == COMMAND_CONFIGURE_DMA);
+ ctrl->command_status = ret ? COMMAND_STATUS_ERROR :
+ COMMAND_STATUS_OK;
+ break;
case COMMAND_LINK_UP:
ntb->linkup = true;
ret = epf_ntb_link_up(ntb, true);
@@ -459,9 +914,8 @@ static void epf_ntb_config_spad_bar_free(struct epf_ntb *ntb)
* region
* @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.
+ * Allocate the control and scratchpad regions, omitting the optional DMA
+ * extension when no channels are exported.
*
* Returns: Zero for success, or an error code in case of failure
*/
@@ -481,7 +935,9 @@ 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 = ntb->dma ? sizeof(struct epf_ntb_ctrl) :
+ offsetof(struct epf_ntb_ctrl, dma);
+ ctrl_size = ALIGN(ctrl_size, sizeof(u32));
spad_size = 2 * spad_count * sizeof(u32);
base = pci_epf_alloc_space(epf, ctrl_size + spad_size,
@@ -507,6 +963,9 @@ static int epf_ntb_config_spad_bar_alloc(struct epf_ntb *ntb)
ntb->reg->db_offset[i] = 0;
}
+ if (ntb->dma)
+ ctrl->dma = ntb->dma->ctrl;
+
return 0;
}
@@ -738,6 +1197,83 @@ static int epf_ntb_db_bar_init(struct epf_ntb *ntb)
static void epf_ntb_mw_bar_clear(struct epf_ntb *ntb, int num_mws);
+static int epf_ntb_dma_bar_init(struct epf_ntb *ntb)
+{
+ const struct pci_epc_features *features;
+ struct epf_ntb_dma *dma = ntb->dma;
+ struct device *dev = ntb->epf->epc->dev.parent;
+ 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 || !dma->num_submap)
+ return;
+
+ barno = ntb->epf_ntb_bar[BAR_DMA];
+ bar = &ntb->epf->bar[barno];
+ pci_epc_clear_bar(ntb->epf->epc, ntb->epf->func_no,
+ ntb->epf->vfunc_no, bar);
+ bar->submap = NULL;
+ bar->num_submap = 0;
+ bar->phys_addr = 0;
+ bar->addr = NULL;
+ bar->size = 0;
+ bar->mem_size = 0;
+ bar->barno = 0;
+ bar->flags = 0;
+}
+
/**
* epf_ntb_db_bar_clear() - Clear doorbell BAR and free memory
* allocated in peer's outbound address space
@@ -877,7 +1413,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;
@@ -976,11 +1513,19 @@ static int epf_ntb_epc_init(struct epf_ntb *ntb)
goto err_mw_bar_init;
}
+ if (ntb->dma && ntb->dma->num_submap) {
+ 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;
}
}
@@ -992,7 +1537,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);
@@ -1015,6 +1561,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);
@@ -1221,10 +1768,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);
@@ -1243,6 +1793,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,
@@ -1743,6 +2294,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");
@@ -1779,6 +2336,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, false);
return ret;
}
@@ -1795,6 +2353,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, true);
pci_unregister_driver(&vntb_pci_driver);
}
--
2.51.0