iommu/s390: add support for IOMMU passthrough
Matthew Rosato
maintainerv5 cover iommu/s390: add support for IOMMU passthrough
This series introduces the ability for certain devices on s390 to bypass a layer of IOMMU via the iommu.passthrough=1 option. In order to enable this, the concept of an identity domain is added to s390-iommu. On s390, IOMMU passthrough is only allowed if indicated via a special bit in s390 CLP data for the associated device group, otherwise we must fall back to dma-iommu.
Changes for v5:
- Fixup error checking for bus_dma_region size
- Add review/test tags
Changes for v4:
- Additional patch to handle IOAT registration within s390-iommu. This fixes an issue with re-registration of identity domain during events like zpci_hot_reset_device
- Fixup page alignment for bus_dma_region setup
- Dropped a few review/test tags due to above changes
Changes for v3:
- Rebase onto 6.13
- fixed bus_dma_region size (Niklas)
Changes for v2:
- Remove ARCH_HAS_PHYS_TO_DMA, use bus_dma_region
- Remove use of def_domain_type, use 1 of 2 ops chosen at init
Matthew Rosato (4):
s390/pci: check for relaxed translation capability s390/pci: store DMA offset in bus_dma_region iommu/s390: handle IOAT registration based on domain iommu/s390: implement iommu passthrough via identity domain
arch/s390/include/asm/pci.h | 4 +- arch/s390/include/asm/pci_clp.h | 4 +- arch/s390/kvm/pci.c | 17 +--- arch/s390/pci/pci.c | 35 ++++---- arch/s390/pci/pci_bus.c | 25 ++++++ arch/s390/pci/pci_clp.c | 1 + arch/s390/pci/pci_sysfs.c | 11 +-- drivers/iommu/s390-iommu.c | 138 ++++++++++++++++++++++++++------ 8 files changed, 168 insertions(+), 67 deletions(-)
Signature · 1 line
2.48.1
Matthew Rosato
maintainerPATCH v5 1/4 s390/pci: check for relaxed translation capability
For each zdev, record whether or not CLP indicates relaxed translation capability for the associated device group.
--- arch/s390/include/asm/pci.h | 2 +- arch/s390/include/asm/pci_clp.h | 4 +++- arch/s390/pci/pci_clp.c | 1 + 3 files changed, 5 insertions(+), 2 deletions(-)
arch/s390/include/asm/pci.h +1 -1
@@ -144,7 +144,7 @@ struct zpci_dev { u8 util_str_avail : 1; u8 irqs_registered : 1; u8 tid_avail : 1; - u8 reserved : 1; + u8 rtr_avail : 1; /* Relaxed translation allowed */unsigned int devfn;/* DEVFN part of the RID*/ u8 pfip[CLP_PFIP_NR_SEGMENTS ];/* pci function internal path */
arch/s390/include/asm/pci_clp.h +3 -1
@@ -156,7 +156,9 @@ struct clp_rsp_query_pci_grp { u16 :4 ; u16 noi : 12; /* number of interrupts */u8 version; - u8 :6 ; + u8 :2 ; + u8 rtr : 1; /* Relaxed translation requirement */ + u8 :3 ; u8 frame : 1; u8 refresh : 1; /* TLB refresh mode */ u16 :3 ;
arch/s390/pci/pci_clp.c +1 -0
@@ -112,6 +112,7 @@ static void clp_store_query_pci_fngrp(struct zpci_dev *zdev, zdev->version = response->version; zdev->maxstbl = response->maxstbl; zdev->dtsm = response->dtsm; + zdev->rtr_avail = response->rtr;switch (response->version) {case 1 :
Signature · 1 line
2.48.1
Matthew Rosato
maintainerPATCH v5 2/4 s390/pci: store DMA offset in bus_dma_region
In reply to Matthew RosatoPCI devices on s390 have a DMA offset that is reported via CLP. In preparation for allowing identity domains, setup the bus_dma_region for all PCI devices using the reported CLP value.
--- arch/s390/pci/pci_bus.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+)
arch/s390/pci/pci_bus.c +25 -0
@@ -19,6 +19,7 @@#include <linux/jump_label.h> #include <linux/pci.h> #include <linux/printk.h> +#include <linux/dma-direct.h> #include <asm/pci_clp.h> #include <asm/pci_dma.h>
@@ -283,10 +284,34 @@ static struct zpci_bus *zpci_bus_alloc(int topo, bool topo_is_tid)return zbus; } +static void pci_dma_range_setup (struct pci_dev *pdev) +{ +struct zpci_dev *zdev =to_zpci (pdev); +struct bus_dma_region *map; +u64 aligned_end; + + map =kzalloc (sizeof (*map),GFP_KERNEL ); +if (!map) +return ; + + map->cpu_start =0 ; + map->dma_start =PAGE_ALIGN (zdev->start_dma); + aligned_end =PAGE_ALIGN_DOWN (zdev->end_dma +1 ); +if (aligned_end >= map->dma_start) + map->size = aligned_end - map->dma_start; +else + map->size =0 ; +WARN_ON_ONCE (map->size ==0 ); + + pdev->dev.dma_range_map = map; +} +void pcibios_bus_add_device (struct pci_dev *pdev) {struct zpci_dev *zdev =to_zpci (pdev); +pci_dma_range_setup (pdev); +/* * With pdev->no_vf_scan the common PCI probing code does not * perform PF/VF linking.
Signature · 1 line
2.48.1
Re: [PATCH v5 2/4] s390/pci: store DMA offset in bus_dma_region
Quoted 46 lines · Matthew Rosato
PCI devices on s390 have a DMA offset that is reported via CLP. In preparation for allowing identity domains, setup the
bus_dma_regionfor all PCI devices using the reported CLP value.Signed-off-by Matthew Rosato <[email protected]>--- arch/s390/pci/pci_bus.c | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+)arch/s390/pci/pci_bus.c +22
-0@@ -19,6 +19,7 @@#include <linux/jump_label.h> #include <linux/pci.h> #include <linux/printk.h> +#include <linux/dma-direct.h> #include <asm/pci_clp.h> #include <asm/pci_dma.h> @@ -283,10 +284,34 @@ static struct zpci_bus *zpci_bus_alloc(int topo, bool topo_is_tid)return zbus; } +static void pci_dma_range_setup (struct pci_dev *pdev) +{ +struct zpci_dev *zdev =to_zpci (pdev); +struct bus_dma_region *map; +u64 aligned_end; + + map =kzalloc (sizeof (*map),GFP_KERNEL ); +if (!map) +return ; + + map->cpu_start =0 ; + map->dma_start =PAGE_ALIGN (zdev->start_dma); + aligned_end =PAGE_ALIGN_DOWN (zdev->end_dma +1 ); +if (aligned_end >= map->dma_start) + map->size = aligned_end - map->dma_start; +else + map->size =0 ; +WARN_ON_ONCE (map->size ==0 ); + + pdev->dev.dma_range_map = map; +}
arch/s390/pci/pci_bus.c static struct zpci_bus *zpci_bus_alloc(int topo, bool topo_is_tid) · line 317
Quoted 10 lines
arch/s390/pci/pci_bus.c +3
-0+void pcibios_bus_add_device (struct pci_dev *pdev) {struct zpci_dev *zdev =to_zpci (pdev); +pci_dma_range_setup (pdev); +/* * With pdev->no_vf_scan the common PCI probing code does not * perform PF/VF linking.
Looks good to me and I tested it again. So feel free to add my:
Matthew Rosato
maintainerPATCH v5 3/4 iommu/s390: handle IOAT registration based on domain
In reply to Matthew RosatoAt this point, the dma_table is really a property of the s390-iommu domain. Rather than checking its contents elsewhere in the codebase, move the code that registers the table with firmware into s390-iommu and make a decision what to register with firmware based upon the type of domain in use for the device in question.
--- arch/s390/include/asm/pci.h | 2 ++ arch/s390/kvm/pci.c | 17 ++------------- arch/s390/pci/pci.c | 35 +++++++++++++++++------------- arch/s390/pci/pci_sysfs.c | 11 +--------- drivers/iommu/s390-iommu.c | 43 +++++++++++++++++++++++++++++++++++-- 5 files changed, 66 insertions(+), 42 deletions(-)
arch/s390/include/asm/pci.h +2 -0
@@ -217,6 +217,7 @@ extern struct airq_iv *zpci_aif_sbv;struct zpci_dev *zpci_create_device (u32 fid,u32 fh,enum zpci_state state);int zpci_add_device (struct zpci_dev *zdev);int zpci_enable_device (struct zpci_dev *); +int zpci_reenable_device (struct zpci_dev *zdev);int zpci_disable_device (struct zpci_dev *);int zpci_scan_configured_device (struct zpci_dev *zdev,u32 fh);int zpci_deconfigure_device (struct zpci_dev *zdev);
@@ -245,6 +246,7 @@ void update_uid_checking(bool new);/* IOMMU Interface */ int zpci_init_iommu (struct zpci_dev *zdev);void zpci_destroy_iommu (struct zpci_dev *zdev); +int zpci_iommu_register_ioat (struct zpci_dev *zdev,u8 *status);#ifdef CONFIG_PCI static inline bool zpci_use_mio (struct zpci_dev *zdev)
arch/s390/kvm/pci.c +2 -15
@@ -433,7 +433,6 @@ static void kvm_s390_pci_dev_release(struct zpci_dev *zdev)static int kvm_s390_pci_register_kvm (void *opaque,struct kvm *kvm) {struct zpci_dev *zdev = opaque; -u8 status;int rc;if (!zdev)
@@ -480,13 +479,7 @@ static int kvm_s390_pci_register_kvm(void *opaque, struct kvm *kvm)*/ zdev->gisa = (u32 )virt_to_phys (&kvm->arch.sie_page2->gisa); - rc =zpci_enable_device (zdev); -if (rc) - goto clear_gisa; - -/* Re-register the IOMMU that was already created */ - rc =zpci_register_ioat (zdev,0 , zdev->start_dma, zdev->end_dma, -virt_to_phys (zdev->dma_table), &status); + rc =zpci_reenable_device (zdev);if (rc) goto clear_gisa;
@@ -516,7 +509,6 @@ static void kvm_s390_pci_unregister_kvm(void *opaque) {struct zpci_dev *zdev = opaque;struct kvm *kvm; -u8 status;if (!zdev)return ;
@@ -550,12 +542,7 @@ static void kvm_s390_pci_unregister_kvm(void *opaque) goto out; } -if (zpci_enable_device (zdev)) - goto out; - -/* Re-register the IOMMU that was already created */ -zpci_register_ioat (zdev,0 , zdev->start_dma, zdev->end_dma, -virt_to_phys (zdev->dma_table), &status); +zpci_reenable_device (zdev); out:spin_lock (&kvm->arch.kzdev_list_lock);
arch/s390/pci/pci.c +20 -15
@@ -124,14 +124,13 @@ int zpci_register_ioat(struct zpci_dev *zdev, u8 dmaas,struct zpci_fib fib = {0 };u8 cc; -WARN_ON_ONCE (iota &0x3fff ); fib.pba = base;/* Work around off by one in ISM virt device */ if (zdev->pft ==PCI_FUNC_TYPE_ISM && limit > base) fib.pal = limit + (1 <<12 );else fib.pal = limit; - fib.iota = iota |ZPCI_IOTA_RTTO_FLAG ; + fib.iota = iota; fib.gd = zdev->gisa; cc =zpci_mod_fc (req, &fib, status);if (cc)
@@ -690,6 +689,23 @@ int zpci_enable_device(struct zpci_dev *zdev) }EXPORT_SYMBOL_GPL (zpci_enable_device); +int zpci_reenable_device (struct zpci_dev *zdev) +{ +u8 status; +int rc; + + rc =zpci_enable_device (zdev); +if (rc) +return rc; + + rc =zpci_iommu_register_ioat (zdev, &status); +if (rc) +zpci_disable_device (zdev); + +return rc; +} +EXPORT_SYMBOL_GPL (zpci_reenable_device); +int zpci_disable_device (struct zpci_dev *zdev) {u32 fh = zdev->fh;
@@ -739,7 +755,6 @@ EXPORT_SYMBOL_GPL(zpci_disable_device);*/ int zpci_hot_reset_device (struct zpci_dev *zdev) { -u8 status;int rc;lockdep_assert_held (&zdev->state_lock);
@@ -758,19 +773,9 @@ int zpci_hot_reset_device(struct zpci_dev *zdev)return rc; } - rc =zpci_enable_device (zdev); -if (rc) -return rc; - -if (zdev->dma_table) - rc =zpci_register_ioat (zdev,0 , zdev->start_dma, zdev->end_dma, -virt_to_phys (zdev->dma_table), &status); -if (rc) { -zpci_disable_device (zdev); -return rc; - } + rc =zpci_reenable_device (zdev); -return 0 ; +return rc; }/**
arch/s390/pci/pci_sysfs.c +1 -10
@@ -52,7 +52,6 @@ static DEVICE_ATTR_RO(mio_enabled);static int _do_recover (struct pci_dev *pdev,struct zpci_dev *zdev) { -u8 status;int ret;pci_stop_and_remove_bus_device (pdev);
@@ -70,16 +69,8 @@ static int _do_recover(struct pci_dev *pdev, struct zpci_dev *zdev)return ret; } - ret =zpci_enable_device (zdev); -if (ret) -return ret; + ret =zpci_reenable_device (zdev); -if (zdev->dma_table) { - ret =zpci_register_ioat (zdev,0 , zdev->start_dma, zdev->end_dma, -virt_to_phys (zdev->dma_table), &status); -if (ret) -zpci_disable_device (zdev); - }return ret; }
drivers/iommu/s390-iommu.c +41 -2
55 lines not on this page · raw message
Signature · 1 line
2.48.1
Matthew Rosato
maintainerPATCH v5 4/4 iommu/s390: implement iommu passthrough via identity domain
In reply to Matthew RosatoEnabled via the kernel command-line 'iommu.passthrough=1' option.
Introduce the concept of identity domains to s390-iommu, which relies on the bus_dma_region to offset identity mappings to the start of the DMA aperture advertized by CLP.
--- drivers/iommu/s390-iommu.c | 95 +++++++++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 23 deletions(-)
drivers/iommu/s390-iommu.c +72 -23
@@ -16,7 +16,7 @@#include "dma-iommu.h" -static const struct iommu_ops s390_iommu_ops; +static const struct iommu_ops s390_iommu_ops, s390_iommu_rtr_ops;static struct kmem_cache *dma_region_table_cache;static struct kmem_cache *dma_page_table_cache;
@@ -432,9 +432,11 @@ static int blocking_domain_attach_device(struct iommu_domain *domain,return 0 ; s390_domain =to_s390_domain (zdev->s390_domain); -spin_lock_irqsave (&s390_domain->list_lock, flags); -list_del_rcu (&zdev->iommu_list); -spin_unlock_irqrestore (&s390_domain->list_lock, flags); +if (zdev->dma_table) { +spin_lock_irqsave (&s390_domain->list_lock, flags); +list_del_rcu (&zdev->iommu_list); +spin_unlock_irqrestore (&s390_domain->list_lock, flags); + }zpci_unregister_ioat (zdev,0 ); zdev->dma_table =NULL ;
@@ -762,7 +764,13 @@ int zpci_init_iommu(struct zpci_dev *zdev)if (rc) goto out_err; - rc =iommu_device_register (&zdev->iommu_dev, &s390_iommu_ops,NULL ); +if (zdev->rtr_avail) { + rc =iommu_device_register (&zdev->iommu_dev, + &s390_iommu_rtr_ops,NULL ); + }else { + rc =iommu_device_register (&zdev->iommu_dev, &s390_iommu_ops, +NULL ); + }if (rc) goto out_sysfs;
@@ -826,6 +834,39 @@ static int __init s390_iommu_init(void) }subsys_initcall (s390_iommu_init); +static int s390_attach_dev_identity (struct iommu_domain *domain, +struct device *dev) +{ +struct zpci_dev *zdev =to_zpci_dev (dev); +u8 status; +int cc; + +blocking_domain_attach_device (&blocking_domain, dev); + +/* If we fail now DMA remains blocked via blocking domain */ + cc =s390_iommu_domain_reg_ioat (zdev, domain, &status); + +/* +* If the device is undergoing error recovery the reset code +* will re-establish the new domain. +*/ +if (cc && status !=ZPCI_PCI_ST_FUNC_NOT_AVAIL ) +return -EIO ; + +zdev_s390_domain_update (zdev, domain); + +return 0 ; +} + +static const struct iommu_domain_ops s390_identity_ops = { + .attach_dev = s390_attach_dev_identity, +}; + +static struct iommu_domain s390_identity_domain = { + .type =IOMMU_DOMAIN_IDENTITY , + .ops = &s390_identity_ops, +}; +static struct iommu_domain blocking_domain = { .type =IOMMU_DOMAIN_BLOCKED , .ops = &(const struct iommu_domain_ops ) {
@@ -833,23 +874,31 @@ static struct iommu_domain blocking_domain = { } }; -static const struct iommu_ops s390_iommu_ops = { - .blocked_domain = &blocking_domain, - .release_domain = &blocking_domain, - .capable = s390_iommu_capable, - .domain_alloc_paging = s390_domain_alloc_paging, - .probe_device = s390_iommu_probe_device, - .device_group = generic_device_group, - .pgsize_bitmap =SZ_4K , - .get_resv_regions = s390_iommu_get_resv_regions, - .default_domain_ops = &(const struct iommu_domain_ops ) { - .attach_dev = s390_iommu_attach_device, - .map_pages = s390_iommu_map_pages, - .unmap_pages = s390_iommu_unmap_pages, - .flush_iotlb_all = s390_iommu_flush_iotlb_all, - .iotlb_sync = s390_iommu_iotlb_sync, - .iotlb_sync_map = s390_iommu_iotlb_sync_map, - .iova_to_phys = s390_iommu_iova_to_phys, - .free = s390_domain_free, +#define S390_IOMMU_COMMON_OPS () \ + .blocked_domain = &blocking_domain, \ + .release_domain = &blocking_domain, \ + .capable = s390_iommu_capable, \ + .domain_alloc_paging = s390_domain_alloc_paging, \ + .probe_device = s390_iommu_probe_device, \ + .device_group = generic_device_group, \ + .pgsize_bitmap = SZ_4K, \ + .get_resv_regions = s390_iommu_get_resv_regions, \ + .default_domain_ops = &(const struct iommu_domain_ops) { \ + .attach_dev = s390_iommu_attach_device, \ + .map_pages = s390_iommu_map_pages, \ + .unmap_pages = s390_iommu_unmap_pages, \ + .flush_iotlb_all = s390_iommu_flush_iotlb_all, \ + .iotlb_sync = s390_iommu_iotlb_sync, \ + .iotlb_sync_map = s390_iommu_iotlb_sync_map, \ + .iova_to_phys = s390_iommu_iova_to_phys, \ + .free = s390_domain_free, \ } + +static const struct iommu_ops s390_iommu_ops = { +S390_IOMMU_COMMON_OPS () +}; + +static const struct iommu_ops s390_iommu_rtr_ops = { + .identity_domain = &s390_identity_domain, +S390_IOMMU_COMMON_OPS () };
Signature · 1 line
2.48.1
Re: [PATCH v5 4/4] iommu/s390: implement iommu passthrough via identity domain
Quoted 6 lines · Matthew Rosato
Enabled via the kernel command-line 'iommu.passthrough=1' option.
Introduce the concept of identity domains to s390-iommu, which relies on the
bus_dma_regionto offset identity mappings to the start of the DMA aperture advertized by CLP.
Quoted 6 lines
Tested-by Niklas Schnelle <[email protected]>Reviewed-by Niklas Schnelle <[email protected]>Signed-off-by Matthew Rosato <[email protected]>--- drivers/iommu/s390-iommu.c | 95 +++++++++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 23 deletions(-)
This may want to be changed to use
https://lore.kernel.org/linux-iommu/[email protected]/
Later on
Jason
Matthew Rosato
maintainerRe: [PATCH v5 4/4] iommu/s390: implement iommu passthrough via identity domain
Quoted 14 lines · Jason Gunthorpe
Quoted 6 lines · Matthew Rosato
Enabled via the kernel command-line 'iommu.passthrough=1' option.
Introduce the concept of identity domains to s390-iommu, which relies on the
bus_dma_regionto offset identity mappings to the start of the DMA aperture advertized by CLP.Quoted 6 lines
Tested-by Niklas Schnelle <[email protected]>Reviewed-by Niklas Schnelle <[email protected]>Signed-off-by Matthew Rosato <[email protected]>--- drivers/iommu/s390-iommu.c | 95 +++++++++++++++++++++++++++++--------- 1 file changed, 72 insertions(+), 23 deletions(-)
Quoted 8 lines
Reviewed-by Jason Gunthorpe <[email protected]>This may want to be changed to use
https://lore.kernel.org/linux-iommu/[email protected]/
Later on
Thanks for reviewing again. And ACK, I will look at this for follow-on
Quoted 32 lines · Matthew Rosato
This series introduces the ability for certain devices on s390 to bypass a layer of IOMMU via the iommu.passthrough=1 option. In order to enable this, the concept of an identity domain is added to s390-iommu. On s390, IOMMU passthrough is only allowed if indicated via a special bit in s390 CLP data for the associated device group, otherwise we must fall back to dma-iommu.
Changes for v5:
- Fixup error checking forbus_dma_regionsize
- Add review/test tagsChanges for v4:
- Additional patch to handle IOAT registration within s390-iommu. This fixes an issue with re-registration of identity domain during events likezpci_hot_reset_device
- Fixup page alignment forbus_dma_regionsetup
- Dropped a few review/test tags due to above changesChanges for v3:
- Rebase onto 6.13
- fixedbus_dma_regionsize (Niklas)Changes for v2:
- RemoveARCH_HAS_PHYS_TO_DMA, usebus_dma_region
- Remove use ofdef_domain_type, use 1 of 2 ops chosen at initMatthew Rosato (4):
s390/pci: check for relaxed translation capability s390/pci: store DMA offset inbus_dma_regioniommu/s390: handle IOAT registration based on domain iommu/s390: implement iommu passthrough via identity domain
Quoted 9 lines
arch/s390/include/asm/pci.h | 4 +- arch/s390/include/asm/pci_clp.h | 4 +- arch/s390/kvm/pci.c | 17 +--- arch/s390/pci/pci.c | 35 ++++---- arch/s390/pci/pci_bus.c | 25 ++++++ arch/s390/pci/pci_clp.c | 1 + arch/s390/pci/pci_sysfs.c | 11 +-- drivers/iommu/s390-iommu.c | 138 ++++++++++++++++++++++++++------ 8 files changed, 168 insertions(+), 67 deletions(-)
Applied, thanks.