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 |
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 |
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
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 --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;
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(+)