diff mbox series

[net-next,10/11] sfc: implement vdpa config_ops for dma operations

Message ID 20221207145428.31544-11-gautam.dawar@amd.com (mailing list archive)
State Changes Requested
Delegated to: Netdev Maintainers
Headers show
Series sfc: add vDPA support for EF100 devices | expand

Checks

Context Check Description
netdev/tree_selection success Clearly marked for net-next
netdev/fixes_present success Fixes tag not required for -next series
netdev/subject_prefix success Link
netdev/cover_letter success Series has a cover letter
netdev/patch_count success Link
netdev/header_inline success No static functions without inline keyword in header files
netdev/build_32bit fail Errors and warnings before: 2 this patch: 3
netdev/cc_maintainers success CCed 7 of 7 maintainers
netdev/build_clang success Errors and warnings before: 4 this patch: 4
netdev/module_param success Was 0 now: 0
netdev/verify_signedoff success Signed-off-by tag matches author and committer
netdev/check_selftest success No net selftest shell script
netdev/verify_fixes success No Fixes tag
netdev/build_allmodconfig_warn success Errors and warnings before: 2 this patch: 2
netdev/checkpatch success total: 0 errors, 0 warnings, 0 checks, 327 lines checked
netdev/kdoc success Errors and warnings before: 58 this patch: 58
netdev/source_inline success Was 0 now: 0

Commit Message

Gautam Dawar Dec. 7, 2022, 2:54 p.m. UTC
Although sfc uses the platform IOMMU but it still
implements the DMA config operations to deal with
possible IOVA overlap with the MCDI DMA buffer and
relocates the latter if such overlap is detected.

Signed-off-by: Gautam Dawar <gautam.dawar@amd.com>
---
 drivers/net/ethernet/sfc/ef100_vdpa.c     | 140 ++++++++++++++++++++++
 drivers/net/ethernet/sfc/ef100_vdpa.h     |   3 +
 drivers/net/ethernet/sfc/ef100_vdpa_ops.c | 111 +++++++++++++++++
 drivers/net/ethernet/sfc/net_driver.h     |  12 ++
 4 files changed, 266 insertions(+)

Comments

kernel test robot Dec. 7, 2022, 7:23 p.m. UTC | #1
Hi Gautam,

Thank you for the patch! Perhaps something to improve:

[auto build test WARNING on net-next/master]

url:    https://github.com/intel-lab-lkp/linux/commits/Gautam-Dawar/sfc-add-vDPA-support-for-EF100-devices/20221207-230026
patch link:    https://lore.kernel.org/r/20221207145428.31544-11-gautam.dawar%40amd.com
patch subject: [PATCH net-next 10/11] sfc: implement vdpa config_ops for dma operations
config: mips-allyesconfig
compiler: mips-linux-gcc (GCC) 12.1.0
reproduce (this is a W=1 build):
        wget https://raw.githubusercontent.com/intel/lkp-tests/master/sbin/make.cross -O ~/bin/make.cross
        chmod +x ~/bin/make.cross
        # https://github.com/intel-lab-lkp/linux/commit/439c0b3c236e7df427ffe3f4fde5281a1678c08a
        git remote add linux-review https://github.com/intel-lab-lkp/linux
        git fetch --no-tags linux-review Gautam-Dawar/sfc-add-vDPA-support-for-EF100-devices/20221207-230026
        git checkout 439c0b3c236e7df427ffe3f4fde5281a1678c08a
        # save the config file
        mkdir build_dir && cp config build_dir/.config
        COMPILER_INSTALL_PATH=$HOME/0day COMPILER=gcc-12.1.0 make.cross W=1 O=build_dir ARCH=mips SHELL=/bin/bash drivers/net/ethernet/sfc/

If you fix the issue, kindly add following tag where applicable
| Reported-by: kernel test robot <lkp@intel.com>

All warnings (new ones prefixed by >>):

   In file included from include/linux/device.h:15,
                    from include/linux/vdpa.h:6,
                    from drivers/net/ethernet/sfc/ef100_vdpa_ops.c:11:
   drivers/net/ethernet/sfc/ef100_vdpa_ops.c: In function 'ef100_vdpa_dma_map':
>> drivers/net/ethernet/sfc/ef100_vdpa_ops.c:783:26: warning: format '%llx' expects argument of type 'long long unsigned int', but argument 4 has type 'dma_addr_t' {aka 'unsigned int'} [-Wformat=]
     783 |                          "%s: mcdi iova overlap detected: %llx\n",
         |                          ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
   include/linux/dev_printk.h:110:30: note: in definition of macro 'dev_printk_index_wrap'
     110 |                 _p_func(dev, fmt, ##__VA_ARGS__);                       \
         |                              ^~~
   include/linux/dev_printk.h:150:58: note: in expansion of macro 'dev_fmt'
     150 |         dev_printk_index_wrap(_dev_info, KERN_INFO, dev, dev_fmt(fmt), ##__VA_ARGS__)
         |                                                          ^~~~~~~
   drivers/net/ethernet/sfc/ef100_vdpa_ops.c:782:17: note: in expansion of macro 'dev_info'
     782 |                 dev_info(&vdpa_nic->vdpa_dev.dev,
         |                 ^~~~~~~~
   drivers/net/ethernet/sfc/ef100_vdpa_ops.c:783:62: note: format string is defined here
     783 |                          "%s: mcdi iova overlap detected: %llx\n",
         |                                                           ~~~^
         |                                                              |
         |                                                              long long unsigned int
         |                                                           %x


vim +783 drivers/net/ethernet/sfc/ef100_vdpa_ops.c

   747	
   748	static int ef100_vdpa_dma_map(struct vdpa_device *vdev,
   749				      unsigned int asid,
   750				      u64 iova, u64 size,
   751				      u64 pa, u32 perm, void *opaque)
   752	{
   753		struct ef100_vdpa_nic *vdpa_nic;
   754		struct ef100_nic_data *nic_data;
   755		unsigned int mcdi_buf_len;
   756		dma_addr_t mcdi_buf_addr;
   757		u64 mcdi_iova = 0;
   758		int rc;
   759	
   760		vdpa_nic = get_vdpa_nic(vdev);
   761		nic_data = vdpa_nic->efx->nic_data;
   762		mcdi_buf_addr = nic_data->mcdi_buf.dma_addr;
   763		mcdi_buf_len = nic_data->mcdi_buf.len;
   764	
   765		/* Validate the iova range against geo aperture */
   766		if (iova < vdpa_nic->geo_aper_start ||
   767		    ((iova + size - 1) > vdpa_nic->geo_aper_end)) {
   768			dev_err(&vdpa_nic->vdpa_dev.dev,
   769				"%s: iova range (%llx, %llx) not within geo aperture\n",
   770				__func__, iova, (iova + size));
   771			return -EINVAL;
   772		}
   773	
   774		rc = efx_ef100_insert_iova_node(vdpa_nic, iova, size);
   775		if (rc) {
   776			dev_err(&vdpa_nic->vdpa_dev.dev,
   777				"%s: iova_node insert failure: %d\n", __func__, rc);
   778			return rc;
   779		}
   780	
   781		if (is_iova_overlap(mcdi_buf_addr, mcdi_buf_len, iova, size)) {
   782			dev_info(&vdpa_nic->vdpa_dev.dev,
 > 783				 "%s: mcdi iova overlap detected: %llx\n",
   784				 __func__, mcdi_buf_addr);
   785			/* find the new iova for mcdi buffer */
   786			rc = efx_ef100_find_new_iova(vdpa_nic, mcdi_buf_len,
   787						     &mcdi_iova);
   788			if (rc) {
   789				dev_err(&vdpa_nic->vdpa_dev.dev,
   790					"new mcdi iova not found, err: %d\n", rc);
   791				goto fail;
   792			}
   793	
   794			if (vdpa_nic->efx->mcdi_buf_mode == EFX_BUF_MODE_VDPA)
   795				rc = ef100_remap_vdpa_mcdi_buffer(vdpa_nic->efx,
   796								  mcdi_iova);
   797			else if (vdpa_nic->efx->mcdi_buf_mode == EFX_BUF_MODE_EF100)
   798				rc = ef100_setup_vdpa_mcdi_buffer(vdpa_nic->efx,
   799								  mcdi_iova);
   800			else
   801				goto fail;
   802	
   803			if (rc) {
   804				dev_err(&vdpa_nic->vdpa_dev.dev,
   805					"mcdi buf update failed, err: %d\n", rc);
   806				goto fail;
   807			}
   808		}
   809	
   810		rc = iommu_map(vdpa_nic->domain, iova, pa, size, perm);
   811		if (rc) {
   812			dev_err(&vdev->dev,
   813				"%s: iommu_map iova: %llx size: %llx rc: %d\n",
   814				__func__, iova, size, rc);
   815			goto fail;
   816		}
   817	
   818		return 0;
   819	
   820	fail:
   821		efx_ef100_remove_iova_node(vdpa_nic, iova);
   822		return rc;
   823	}
   824
Jason Wang Dec. 14, 2022, 6:46 a.m. UTC | #2
On Wed, Dec 7, 2022 at 10:57 PM Gautam Dawar <gautam.dawar@amd.com> wrote:
>
> Although sfc uses the platform IOMMU but it still
> implements the DMA config operations to deal with
> possible IOVA overlap with the MCDI DMA buffer and
> relocates the latter if such overlap is detected.
>
> Signed-off-by: Gautam Dawar <gautam.dawar@amd.com>
> ---
>  drivers/net/ethernet/sfc/ef100_vdpa.c     | 140 ++++++++++++++++++++++
>  drivers/net/ethernet/sfc/ef100_vdpa.h     |   3 +
>  drivers/net/ethernet/sfc/ef100_vdpa_ops.c | 111 +++++++++++++++++
>  drivers/net/ethernet/sfc/net_driver.h     |  12 ++
>  4 files changed, 266 insertions(+)
>
> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.c b/drivers/net/ethernet/sfc/ef100_vdpa.c
> index b9368eb1acd5..16681d164fd1 100644
> --- a/drivers/net/ethernet/sfc/ef100_vdpa.c
> +++ b/drivers/net/ethernet/sfc/ef100_vdpa.c
> @@ -309,6 +309,140 @@ static int vdpa_update_domain(struct ef100_vdpa_nic *vdpa_nic)
>                                           vdpa_nic->geo_aper_end + 1, 0);
>  }
>
> +static int ef100_vdpa_alloc_buffer(struct efx_nic *efx, struct efx_buffer *buf)
> +{
> +       struct ef100_vdpa_nic *vdpa_nic = efx->vdpa_nic;
> +       struct device *dev = &vdpa_nic->vdpa_dev.dev;
> +       int rc;
> +
> +       buf->addr = kzalloc(buf->len, GFP_KERNEL);
> +       if (!buf->addr)
> +               return -ENOMEM;
> +
> +       rc = iommu_map(vdpa_nic->domain, buf->dma_addr,
> +                      virt_to_phys(buf->addr), buf->len,
> +                      IOMMU_READ | IOMMU_WRITE | IOMMU_CACHE);
> +       if (rc)
> +               dev_err(dev, "iommu_map failed, rc: %d\n", rc);
> +
> +       return rc;
> +}
> +
> +static void ef100_vdpa_free_buffer(struct ef100_vdpa_nic *vdpa_nic,
> +                                  struct efx_buffer *buf)
> +{
> +       struct device *dev = &vdpa_nic->vdpa_dev.dev;
> +       int rc;
> +
> +       rc = iommu_unmap(vdpa_nic->domain, buf->dma_addr, buf->len);
> +       if (rc < 0)
> +               dev_err(dev, "iommu_unmap failed, rc: %d\n", rc);
> +
> +       kfree(buf->addr);
> +}
> +
> +int ef100_setup_ef100_mcdi_buffer(struct ef100_vdpa_nic *vdpa_nic)
> +{
> +       struct efx_nic *efx = vdpa_nic->efx;
> +       struct ef100_nic_data *nic_data;
> +       struct efx_mcdi_iface *mcdi;
> +       struct efx_buffer mcdi_buf;
> +       enum efx_mcdi_mode mode;
> +       struct device *dev;
> +       int rc;
> +
> +       /* Switch to poll mode MCDI mode */
> +       nic_data = efx->nic_data;
> +       dev = &vdpa_nic->vdpa_dev.dev;
> +       mcdi = efx_mcdi(efx);
> +       mode = mcdi->mode;
> +       efx_mcdi_mode_poll(efx);
> +       efx_mcdi_flush_async(efx);
> +
> +       /* First, allocate the MCDI buffer for EF100 mode */
> +       rc = efx_nic_alloc_buffer(efx, &mcdi_buf,
> +                                 MCDI_BUF_LEN, GFP_KERNEL);
> +       if (rc) {
> +               dev_err(dev, "nic alloc buf failed, rc: %d\n", rc);
> +               goto restore_mode;
> +       }
> +
> +       /* unmap and free the vDPA MCDI buffer now */
> +       ef100_vdpa_free_buffer(vdpa_nic, &nic_data->mcdi_buf);
> +       memcpy(&nic_data->mcdi_buf, &mcdi_buf, sizeof(struct efx_buffer));
> +       efx->mcdi_buf_mode = EFX_BUF_MODE_EF100;
> +
> +restore_mode:
> +       if (mode == MCDI_MODE_EVENTS)
> +               efx_mcdi_mode_event(efx);
> +
> +       return rc;
> +}
> +
> +int ef100_setup_vdpa_mcdi_buffer(struct efx_nic *efx, u64 mcdi_iova)
> +{
> +       struct ef100_nic_data *nic_data = efx->nic_data;
> +       struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
> +       enum efx_mcdi_mode mode = mcdi->mode;
> +       struct efx_buffer mcdi_buf;
> +       int rc;
> +
> +       efx_mcdi_mode_poll(efx);
> +       efx_mcdi_flush_async(efx);
> +
> +       /* First, prepare the MCDI buffer for vDPA mode */
> +       mcdi_buf.dma_addr = mcdi_iova;
> +       /* iommu_map requires page aligned memory */
> +       mcdi_buf.len = PAGE_ALIGN(MCDI_BUF_LEN);
> +       rc = ef100_vdpa_alloc_buffer(efx, &mcdi_buf);
> +       if (rc) {
> +               pci_err(efx->pci_dev, "alloc vdpa buf failed, rc: %d\n", rc);
> +               goto restore_mode;
> +       }
> +
> +       /* All set-up, free the EF100 MCDI buffer now */
> +       efx_nic_free_buffer(efx, &nic_data->mcdi_buf);
> +       memcpy(&nic_data->mcdi_buf, &mcdi_buf, sizeof(struct efx_buffer));
> +       efx->mcdi_buf_mode = EFX_BUF_MODE_VDPA;
> +
> +restore_mode:
> +       if (mode == MCDI_MODE_EVENTS)
> +               efx_mcdi_mode_event(efx);
> +       return rc;
> +}
> +
> +int ef100_remap_vdpa_mcdi_buffer(struct efx_nic *efx, u64 mcdi_iova)
> +{
> +       struct ef100_nic_data *nic_data = efx->nic_data;
> +       struct ef100_vdpa_nic *vdpa_nic = efx->vdpa_nic;
> +       struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
> +       struct efx_buffer *mcdi_buf;
> +       int rc;
> +
> +       mcdi_buf = &nic_data->mcdi_buf;
> +       spin_lock_bh(&mcdi->iface_lock);
> +
> +       rc = iommu_unmap(vdpa_nic->domain, mcdi_buf->dma_addr, mcdi_buf->len);
> +       if (rc < 0) {
> +               pci_err(efx->pci_dev, "iommu_unmap failed, rc: %d\n", rc);
> +               goto out;
> +       }
> +
> +       rc = iommu_map(vdpa_nic->domain, mcdi_iova,
> +                      virt_to_phys(mcdi_buf->addr),
> +                      mcdi_buf->len,
> +                      IOMMU_READ | IOMMU_WRITE | IOMMU_CACHE);
> +       if (rc) {
> +               pci_err(efx->pci_dev, "iommu_map failed, rc: %d\n", rc);
> +               goto out;
> +       }
> +
> +       mcdi_buf->dma_addr = mcdi_iova;
> +out:
> +       spin_unlock_bh(&mcdi->iface_lock);
> +       return rc;
> +}
> +
>  static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
>                                                 const char *dev_name,
>                                                 enum ef100_vdpa_class dev_type,
> @@ -391,6 +525,12 @@ static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
>                 goto err_put_device;
>         }
>
> +       rc = ef100_setup_vdpa_mcdi_buffer(efx, EF100_VDPA_IOVA_BASE_ADDR);
> +       if (rc) {
> +               pci_err(efx->pci_dev, "realloc mcdi failed, err: %d\n", rc);
> +               goto err_put_device;
> +       }
> +
>         rc = get_net_config(vdpa_nic);
>         if (rc)
>                 goto err_put_device;
> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.h b/drivers/net/ethernet/sfc/ef100_vdpa.h
> index c3c77029973d..f15d8739dcde 100644
> --- a/drivers/net/ethernet/sfc/ef100_vdpa.h
> +++ b/drivers/net/ethernet/sfc/ef100_vdpa.h
> @@ -202,6 +202,9 @@ int ef100_vdpa_add_filter(struct ef100_vdpa_nic *vdpa_nic,
>  int ef100_vdpa_irq_vectors_alloc(struct pci_dev *pci_dev, u16 nvqs);
>  void ef100_vdpa_irq_vectors_free(void *data);
>  int ef100_vdpa_reset(struct vdpa_device *vdev);
> +int ef100_setup_ef100_mcdi_buffer(struct ef100_vdpa_nic *vdpa_nic);
> +int ef100_setup_vdpa_mcdi_buffer(struct efx_nic *efx, u64 mcdi_iova);
> +int ef100_remap_vdpa_mcdi_buffer(struct efx_nic *efx, u64 mcdi_iova);
>
>  static inline bool efx_vdpa_is_little_endian(struct ef100_vdpa_nic *vdpa_nic)
>  {
> diff --git a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
> index 8c198d949fdb..7c632f179bcf 100644
> --- a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
> +++ b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
> @@ -12,6 +12,7 @@
>  #include "ef100_vdpa.h"
>  #include "ef100_iova.h"
>  #include "io.h"
> +#include "ef100_iova.h"
>  #include "mcdi_vdpa.h"
>
>  /* Get the queue's function-local index of the associated VI
> @@ -739,14 +740,121 @@ static void ef100_vdpa_set_config(struct vdpa_device *vdev, unsigned int offset,
>         }
>  }
>
> +static bool is_iova_overlap(u64 iova1, u64 size1, u64 iova2, u64 size2)
> +{
> +       return max(iova1, iova2) < min(iova1 + size1, iova2 + size2);
> +}
> +
> +static int ef100_vdpa_dma_map(struct vdpa_device *vdev,
> +                             unsigned int asid,
> +                             u64 iova, u64 size,
> +                             u64 pa, u32 perm, void *opaque)
> +{
> +       struct ef100_vdpa_nic *vdpa_nic;
> +       struct ef100_nic_data *nic_data;
> +       unsigned int mcdi_buf_len;
> +       dma_addr_t mcdi_buf_addr;
> +       u64 mcdi_iova = 0;
> +       int rc;
> +
> +       vdpa_nic = get_vdpa_nic(vdev);
> +       nic_data = vdpa_nic->efx->nic_data;
> +       mcdi_buf_addr = nic_data->mcdi_buf.dma_addr;
> +       mcdi_buf_len = nic_data->mcdi_buf.len;
> +
> +       /* Validate the iova range against geo aperture */
> +       if (iova < vdpa_nic->geo_aper_start ||
> +           ((iova + size - 1) > vdpa_nic->geo_aper_end)) {
> +               dev_err(&vdpa_nic->vdpa_dev.dev,
> +                       "%s: iova range (%llx, %llx) not within geo aperture\n",
> +                       __func__, iova, (iova + size));
> +               return -EINVAL;

It might be helpful to advertise this geo via get_iova_range().

Thanks
diff mbox series

Patch

diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.c b/drivers/net/ethernet/sfc/ef100_vdpa.c
index b9368eb1acd5..16681d164fd1 100644
--- a/drivers/net/ethernet/sfc/ef100_vdpa.c
+++ b/drivers/net/ethernet/sfc/ef100_vdpa.c
@@ -309,6 +309,140 @@  static int vdpa_update_domain(struct ef100_vdpa_nic *vdpa_nic)
 					  vdpa_nic->geo_aper_end + 1, 0);
 }
 
+static int ef100_vdpa_alloc_buffer(struct efx_nic *efx, struct efx_buffer *buf)
+{
+	struct ef100_vdpa_nic *vdpa_nic = efx->vdpa_nic;
+	struct device *dev = &vdpa_nic->vdpa_dev.dev;
+	int rc;
+
+	buf->addr = kzalloc(buf->len, GFP_KERNEL);
+	if (!buf->addr)
+		return -ENOMEM;
+
+	rc = iommu_map(vdpa_nic->domain, buf->dma_addr,
+		       virt_to_phys(buf->addr), buf->len,
+		       IOMMU_READ | IOMMU_WRITE | IOMMU_CACHE);
+	if (rc)
+		dev_err(dev, "iommu_map failed, rc: %d\n", rc);
+
+	return rc;
+}
+
+static void ef100_vdpa_free_buffer(struct ef100_vdpa_nic *vdpa_nic,
+				   struct efx_buffer *buf)
+{
+	struct device *dev = &vdpa_nic->vdpa_dev.dev;
+	int rc;
+
+	rc = iommu_unmap(vdpa_nic->domain, buf->dma_addr, buf->len);
+	if (rc < 0)
+		dev_err(dev, "iommu_unmap failed, rc: %d\n", rc);
+
+	kfree(buf->addr);
+}
+
+int ef100_setup_ef100_mcdi_buffer(struct ef100_vdpa_nic *vdpa_nic)
+{
+	struct efx_nic *efx = vdpa_nic->efx;
+	struct ef100_nic_data *nic_data;
+	struct efx_mcdi_iface *mcdi;
+	struct efx_buffer mcdi_buf;
+	enum efx_mcdi_mode mode;
+	struct device *dev;
+	int rc;
+
+	/* Switch to poll mode MCDI mode */
+	nic_data = efx->nic_data;
+	dev = &vdpa_nic->vdpa_dev.dev;
+	mcdi = efx_mcdi(efx);
+	mode = mcdi->mode;
+	efx_mcdi_mode_poll(efx);
+	efx_mcdi_flush_async(efx);
+
+	/* First, allocate the MCDI buffer for EF100 mode */
+	rc = efx_nic_alloc_buffer(efx, &mcdi_buf,
+				  MCDI_BUF_LEN, GFP_KERNEL);
+	if (rc) {
+		dev_err(dev, "nic alloc buf failed, rc: %d\n", rc);
+		goto restore_mode;
+	}
+
+	/* unmap and free the vDPA MCDI buffer now */
+	ef100_vdpa_free_buffer(vdpa_nic, &nic_data->mcdi_buf);
+	memcpy(&nic_data->mcdi_buf, &mcdi_buf, sizeof(struct efx_buffer));
+	efx->mcdi_buf_mode = EFX_BUF_MODE_EF100;
+
+restore_mode:
+	if (mode == MCDI_MODE_EVENTS)
+		efx_mcdi_mode_event(efx);
+
+	return rc;
+}
+
+int ef100_setup_vdpa_mcdi_buffer(struct efx_nic *efx, u64 mcdi_iova)
+{
+	struct ef100_nic_data *nic_data = efx->nic_data;
+	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
+	enum efx_mcdi_mode mode = mcdi->mode;
+	struct efx_buffer mcdi_buf;
+	int rc;
+
+	efx_mcdi_mode_poll(efx);
+	efx_mcdi_flush_async(efx);
+
+	/* First, prepare the MCDI buffer for vDPA mode */
+	mcdi_buf.dma_addr = mcdi_iova;
+	/* iommu_map requires page aligned memory */
+	mcdi_buf.len = PAGE_ALIGN(MCDI_BUF_LEN);
+	rc = ef100_vdpa_alloc_buffer(efx, &mcdi_buf);
+	if (rc) {
+		pci_err(efx->pci_dev, "alloc vdpa buf failed, rc: %d\n", rc);
+		goto restore_mode;
+	}
+
+	/* All set-up, free the EF100 MCDI buffer now */
+	efx_nic_free_buffer(efx, &nic_data->mcdi_buf);
+	memcpy(&nic_data->mcdi_buf, &mcdi_buf, sizeof(struct efx_buffer));
+	efx->mcdi_buf_mode = EFX_BUF_MODE_VDPA;
+
+restore_mode:
+	if (mode == MCDI_MODE_EVENTS)
+		efx_mcdi_mode_event(efx);
+	return rc;
+}
+
+int ef100_remap_vdpa_mcdi_buffer(struct efx_nic *efx, u64 mcdi_iova)
+{
+	struct ef100_nic_data *nic_data = efx->nic_data;
+	struct ef100_vdpa_nic *vdpa_nic = efx->vdpa_nic;
+	struct efx_mcdi_iface *mcdi = efx_mcdi(efx);
+	struct efx_buffer *mcdi_buf;
+	int rc;
+
+	mcdi_buf = &nic_data->mcdi_buf;
+	spin_lock_bh(&mcdi->iface_lock);
+
+	rc = iommu_unmap(vdpa_nic->domain, mcdi_buf->dma_addr, mcdi_buf->len);
+	if (rc < 0) {
+		pci_err(efx->pci_dev, "iommu_unmap failed, rc: %d\n", rc);
+		goto out;
+	}
+
+	rc = iommu_map(vdpa_nic->domain, mcdi_iova,
+		       virt_to_phys(mcdi_buf->addr),
+		       mcdi_buf->len,
+		       IOMMU_READ | IOMMU_WRITE | IOMMU_CACHE);
+	if (rc) {
+		pci_err(efx->pci_dev, "iommu_map failed, rc: %d\n", rc);
+		goto out;
+	}
+
+	mcdi_buf->dma_addr = mcdi_iova;
+out:
+	spin_unlock_bh(&mcdi->iface_lock);
+	return rc;
+}
+
 static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
 						const char *dev_name,
 						enum ef100_vdpa_class dev_type,
@@ -391,6 +525,12 @@  static struct ef100_vdpa_nic *ef100_vdpa_create(struct efx_nic *efx,
 		goto err_put_device;
 	}
 
+	rc = ef100_setup_vdpa_mcdi_buffer(efx, EF100_VDPA_IOVA_BASE_ADDR);
+	if (rc) {
+		pci_err(efx->pci_dev, "realloc mcdi failed, err: %d\n", rc);
+		goto err_put_device;
+	}
+
 	rc = get_net_config(vdpa_nic);
 	if (rc)
 		goto err_put_device;
diff --git a/drivers/net/ethernet/sfc/ef100_vdpa.h b/drivers/net/ethernet/sfc/ef100_vdpa.h
index c3c77029973d..f15d8739dcde 100644
--- a/drivers/net/ethernet/sfc/ef100_vdpa.h
+++ b/drivers/net/ethernet/sfc/ef100_vdpa.h
@@ -202,6 +202,9 @@  int ef100_vdpa_add_filter(struct ef100_vdpa_nic *vdpa_nic,
 int ef100_vdpa_irq_vectors_alloc(struct pci_dev *pci_dev, u16 nvqs);
 void ef100_vdpa_irq_vectors_free(void *data);
 int ef100_vdpa_reset(struct vdpa_device *vdev);
+int ef100_setup_ef100_mcdi_buffer(struct ef100_vdpa_nic *vdpa_nic);
+int ef100_setup_vdpa_mcdi_buffer(struct efx_nic *efx, u64 mcdi_iova);
+int ef100_remap_vdpa_mcdi_buffer(struct efx_nic *efx, u64 mcdi_iova);
 
 static inline bool efx_vdpa_is_little_endian(struct ef100_vdpa_nic *vdpa_nic)
 {
diff --git a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
index 8c198d949fdb..7c632f179bcf 100644
--- a/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
+++ b/drivers/net/ethernet/sfc/ef100_vdpa_ops.c
@@ -12,6 +12,7 @@ 
 #include "ef100_vdpa.h"
 #include "ef100_iova.h"
 #include "io.h"
+#include "ef100_iova.h"
 #include "mcdi_vdpa.h"
 
 /* Get the queue's function-local index of the associated VI
@@ -739,14 +740,121 @@  static void ef100_vdpa_set_config(struct vdpa_device *vdev, unsigned int offset,
 	}
 }
 
+static bool is_iova_overlap(u64 iova1, u64 size1, u64 iova2, u64 size2)
+{
+	return max(iova1, iova2) < min(iova1 + size1, iova2 + size2);
+}
+
+static int ef100_vdpa_dma_map(struct vdpa_device *vdev,
+			      unsigned int asid,
+			      u64 iova, u64 size,
+			      u64 pa, u32 perm, void *opaque)
+{
+	struct ef100_vdpa_nic *vdpa_nic;
+	struct ef100_nic_data *nic_data;
+	unsigned int mcdi_buf_len;
+	dma_addr_t mcdi_buf_addr;
+	u64 mcdi_iova = 0;
+	int rc;
+
+	vdpa_nic = get_vdpa_nic(vdev);
+	nic_data = vdpa_nic->efx->nic_data;
+	mcdi_buf_addr = nic_data->mcdi_buf.dma_addr;
+	mcdi_buf_len = nic_data->mcdi_buf.len;
+
+	/* Validate the iova range against geo aperture */
+	if (iova < vdpa_nic->geo_aper_start ||
+	    ((iova + size - 1) > vdpa_nic->geo_aper_end)) {
+		dev_err(&vdpa_nic->vdpa_dev.dev,
+			"%s: iova range (%llx, %llx) not within geo aperture\n",
+			__func__, iova, (iova + size));
+		return -EINVAL;
+	}
+
+	rc = efx_ef100_insert_iova_node(vdpa_nic, iova, size);
+	if (rc) {
+		dev_err(&vdpa_nic->vdpa_dev.dev,
+			"%s: iova_node insert failure: %d\n", __func__, rc);
+		return rc;
+	}
+
+	if (is_iova_overlap(mcdi_buf_addr, mcdi_buf_len, iova, size)) {
+		dev_info(&vdpa_nic->vdpa_dev.dev,
+			 "%s: mcdi iova overlap detected: %llx\n",
+			 __func__, mcdi_buf_addr);
+		/* find the new iova for mcdi buffer */
+		rc = efx_ef100_find_new_iova(vdpa_nic, mcdi_buf_len,
+					     &mcdi_iova);
+		if (rc) {
+			dev_err(&vdpa_nic->vdpa_dev.dev,
+				"new mcdi iova not found, err: %d\n", rc);
+			goto fail;
+		}
+
+		if (vdpa_nic->efx->mcdi_buf_mode == EFX_BUF_MODE_VDPA)
+			rc = ef100_remap_vdpa_mcdi_buffer(vdpa_nic->efx,
+							  mcdi_iova);
+		else if (vdpa_nic->efx->mcdi_buf_mode == EFX_BUF_MODE_EF100)
+			rc = ef100_setup_vdpa_mcdi_buffer(vdpa_nic->efx,
+							  mcdi_iova);
+		else
+			goto fail;
+
+		if (rc) {
+			dev_err(&vdpa_nic->vdpa_dev.dev,
+				"mcdi buf update failed, err: %d\n", rc);
+			goto fail;
+		}
+	}
+
+	rc = iommu_map(vdpa_nic->domain, iova, pa, size, perm);
+	if (rc) {
+		dev_err(&vdev->dev,
+			"%s: iommu_map iova: %llx size: %llx rc: %d\n",
+			__func__, iova, size, rc);
+		goto fail;
+	}
+
+	return 0;
+
+fail:
+	efx_ef100_remove_iova_node(vdpa_nic, iova);
+	return rc;
+}
+
+static int ef100_vdpa_dma_unmap(struct vdpa_device *vdev,
+				unsigned int asid,
+				u64 iova, u64 size)
+{
+	struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
+	int rc;
+
+	rc = iommu_unmap(vdpa_nic->domain, iova, size);
+	if (rc < 0)
+		dev_info(&vdev->dev,
+			 "%s: iommu_unmap iova: %llx size: %llx rc: %d\n",
+			 __func__, iova, size, rc);
+	efx_ef100_remove_iova_node(vdpa_nic, iova);
+	return rc;
+}
+
 static void ef100_vdpa_free(struct vdpa_device *vdev)
 {
 	struct ef100_vdpa_nic *vdpa_nic = get_vdpa_nic(vdev);
+	int rc;
 	int i;
 
 	if (vdpa_nic) {
 		/* clean-up the mappings and iova tree */
 		efx_ef100_delete_iova(vdpa_nic);
+		if (vdpa_nic->efx->mcdi_buf_mode == EFX_BUF_MODE_VDPA) {
+			rc = ef100_setup_ef100_mcdi_buffer(vdpa_nic);
+			if (rc) {
+				dev_err(&vdev->dev,
+					"setup_ef100_mcdi failed, err: %d\n",
+					rc);
+			}
+		}
 		for (i = 0; i < (vdpa_nic->max_queue_pairs * 2); i++)
 			reset_vring(vdpa_nic, i);
 		ef100_vdpa_irq_vectors_free(vdpa_nic->efx->pci_dev);
@@ -782,5 +890,8 @@  const struct vdpa_config_ops ef100_vdpa_config_ops = {
 	.get_config	     = ef100_vdpa_get_config,
 	.set_config	     = ef100_vdpa_set_config,
 	.get_generation      = NULL,
+	.set_map             = NULL,
+	.dma_map	     = ef100_vdpa_dma_map,
+	.dma_unmap           = ef100_vdpa_dma_unmap,
 	.free	             = ef100_vdpa_free,
 };
diff --git a/drivers/net/ethernet/sfc/net_driver.h b/drivers/net/ethernet/sfc/net_driver.h
index 79356d614109..34b94372d9a6 100644
--- a/drivers/net/ethernet/sfc/net_driver.h
+++ b/drivers/net/ethernet/sfc/net_driver.h
@@ -846,6 +846,16 @@  enum efx_xdp_tx_queues_mode {
 	EFX_XDP_TX_QUEUES_BORROWED	/* queues borrowed from net stack */
 };
 
+/**
+ * enum efx_buf_alloc_mode - buffer allocation mode
+ * @EFX_BUF_MODE_EF100: buffer setup in ef100 mode
+ * @EFX_BUF_MODE_VDPA: buffer setup in vdpa mode
+ */
+enum efx_buf_alloc_mode {
+	EFX_BUF_MODE_EF100,
+	EFX_BUF_MODE_VDPA
+};
+
 /**
  * struct efx_nic - an Efx NIC
  * @name: Device name (net device name or bus id before net device registered)
@@ -997,6 +1007,7 @@  enum efx_xdp_tx_queues_mode {
  * @tc: state for TC offload (EF100).
  * @mem_bar: The BAR that is mapped into membase.
  * @reg_base: Offset from the start of the bar to the function control window.
+ * @mcdi_buf_mode: mcdi buffer allocation mode
  * @monitor_work: Hardware monitor workitem
  * @biu_lock: BIU (bus interface unit) lock
  * @last_irq_cpu: Last CPU to handle a possible test interrupt.  This
@@ -1182,6 +1193,7 @@  struct efx_nic {
 
 	unsigned int mem_bar;
 	u32 reg_base;
+	enum efx_buf_alloc_mode mcdi_buf_mode;
 #ifdef CONFIG_SFC_VDPA
 	/** @mgmt_dev: vDPA Management device */
 	struct vdpa_mgmt_dev *mgmt_dev;