Commit 8c9c2f85 authored by Linus Torvalds's avatar Linus Torvalds

Merge tag 'iommu-updates-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu

Pull iommu updates from Joerg Roedel:
 "Core changes:
    - Constification of bus_type pointer
    - Preparations for user-space page-fault delivery
    - Use a named kmem_cache for IOVA magazines

  Intel VT-d changes from Lu Baolu:
    - Add RBTree to track iommu probed devices
    - Add Intel IOMMU debugfs document
    - Cleanup and refactoring

  ARM-SMMU Updates from Will Deacon:
    - Device-tree binding updates for a bunch of Qualcomm SoCs
    - SMMUv2: Support for Qualcomm X1E80100 MDSS
    - SMMUv3: Significant rework of the driver's STE manipulation and
      domain handling code. This is the initial part of a larger scale
      rework aiming to improve the driver's implementation of the
      IOMMU-API in preparation for hooking up IOMMUFD support.

  AMD-Vi Updates:
    - Refactor GCR3 table support for SVA
    - Cleanups

  Some smaller cleanups and fixes"

* tag 'iommu-updates-v6.9' of git://git.kernel.org/pub/scm/linux/kernel/git/joro/iommu: (88 commits)
  iommu: Fix compilation without CONFIG_IOMMU_INTEL
  iommu/amd: Fix sleeping in atomic context
  iommu/dma: Document min_align_mask assumption
  iommu/vt-d: Remove scalabe mode in domain_context_clear_one()
  iommu/vt-d: Remove scalable mode context entry setup from attach_dev
  iommu/vt-d: Setup scalable mode context entry in probe path
  iommu/vt-d: Fix NULL domain on device release
  iommu: Add static iommu_ops->release_domain
  iommu/vt-d: Improve ITE fault handling if target device isn't present
  iommu/vt-d: Don't issue ATS Invalidation request when device is disconnected
  PCI: Make pci_dev_is_disconnected() helper public for other drivers
  iommu/vt-d: Use device rbtree in iopf reporting path
  iommu/vt-d: Use rbtree to track iommu probed devices
  iommu/vt-d: Merge intel_svm_bind_mm() into its caller
  iommu/vt-d: Remove initialization for dynamically heap-allocated rcu_head
  iommu/vt-d: Remove treatment for revoking PASIDs with pending page faults
  iommu/vt-d: Add the document for Intel IOMMU debugfs
  iommu/vt-d: Use kcalloc() instead of kzalloc()
  iommu/vt-d: Remove INTEL_IOMMU_BROKEN_GFX_WA
  iommu: re-use local fwnode variable in iommu_ops_from_fwnode()
  ...
parents b0546776 f379a7e9
What: /sys/kernel/debug/iommu/intel/iommu_regset
Date: December 2023
Contact: Jingqi Liu <Jingqi.liu@intel.com>
Description:
This file dumps all the register contents for each IOMMU device.
Example in Kabylake:
::
$ sudo cat /sys/kernel/debug/iommu/intel/iommu_regset
IOMMU: dmar0 Register Base Address: 26be37000
Name Offset Contents
VER 0x00 0x0000000000000010
GCMD 0x18 0x0000000000000000
GSTS 0x1c 0x00000000c7000000
FSTS 0x34 0x0000000000000000
FECTL 0x38 0x0000000000000000
[...]
IOMMU: dmar1 Register Base Address: fed90000
Name Offset Contents
VER 0x00 0x0000000000000010
GCMD 0x18 0x0000000000000000
GSTS 0x1c 0x00000000c7000000
FSTS 0x34 0x0000000000000000
FECTL 0x38 0x0000000000000000
[...]
IOMMU: dmar2 Register Base Address: fed91000
Name Offset Contents
VER 0x00 0x0000000000000010
GCMD 0x18 0x0000000000000000
GSTS 0x1c 0x00000000c7000000
FSTS 0x34 0x0000000000000000
FECTL 0x38 0x0000000000000000
[...]
What: /sys/kernel/debug/iommu/intel/ir_translation_struct
Date: December 2023
Contact: Jingqi Liu <Jingqi.liu@intel.com>
Description:
This file dumps the table entries for Interrupt
remapping and Interrupt posting.
Example in Kabylake:
::
$ sudo cat /sys/kernel/debug/iommu/intel/ir_translation_struct
Remapped Interrupt supported on IOMMU: dmar0
IR table address:100900000
Entry SrcID DstID Vct IRTE_high IRTE_low
0 00:0a.0 00000080 24 0000000000040050 000000800024000d
1 00:0a.0 00000001 ef 0000000000040050 0000000100ef000d
Remapped Interrupt supported on IOMMU: dmar1
IR table address:100300000
Entry SrcID DstID Vct IRTE_high IRTE_low
0 00:02.0 00000002 26 0000000000040010 000000020026000d
[...]
****
Posted Interrupt supported on IOMMU: dmar0
IR table address:100900000
Entry SrcID PDA_high PDA_low Vct IRTE_high IRTE_low
What: /sys/kernel/debug/iommu/intel/dmar_translation_struct
Date: December 2023
Contact: Jingqi Liu <Jingqi.liu@intel.com>
Description:
This file dumps Intel IOMMU DMA remapping tables, such
as root table, context table, PASID directory and PASID
table entries in debugfs. For legacy mode, it doesn't
support PASID, and hence PASID field is defaulted to
'-1' and other PASID related fields are invalid.
Example in Kabylake:
::
$ sudo cat /sys/kernel/debug/iommu/intel/dmar_translation_struct
IOMMU dmar1: Root Table Address: 0x103027000
B.D.F Root_entry
00:02.0 0x0000000000000000:0x000000010303e001
Context_entry
0x0000000000000102:0x000000010303f005
PASID PASID_table_entry
-1 0x0000000000000000:0x0000000000000000:0x0000000000000000
IOMMU dmar0: Root Table Address: 0x103028000
B.D.F Root_entry
00:0a.0 0x0000000000000000:0x00000001038a7001
Context_entry
0x0000000000000000:0x0000000103220e7d
PASID PASID_table_entry
0 0x0000000000000000:0x0000000000800002:0x00000001038a5089
[...]
What: /sys/kernel/debug/iommu/intel/invalidation_queue
Date: December 2023
Contact: Jingqi Liu <Jingqi.liu@intel.com>
Description:
This file exports invalidation queue internals of each
IOMMU device.
Example in Kabylake:
::
$ sudo cat /sys/kernel/debug/iommu/intel/invalidation_queue
Invalidation queue on IOMMU: dmar0
Base: 0x10022e000 Head: 20 Tail: 20
Index qw0 qw1 qw2
0 0000000000000014 0000000000000000 0000000000000000
1 0000000200000025 0000000100059c04 0000000000000000
2 0000000000000014 0000000000000000 0000000000000000
qw3 status
0000000000000000 0000000000000000
0000000000000000 0000000000000000
0000000000000000 0000000000000000
[...]
Invalidation queue on IOMMU: dmar1
Base: 0x10026e000 Head: 32 Tail: 32
Index qw0 qw1 status
0 0000000000000004 0000000000000000 0000000000000000
1 0000000200000025 0000000100059804 0000000000000000
2 0000000000000011 0000000000000000 0000000000000000
[...]
What: /sys/kernel/debug/iommu/intel/dmar_perf_latency
Date: December 2023
Contact: Jingqi Liu <Jingqi.liu@intel.com>
Description:
This file is used to control and show counts of
execution time ranges for various types per DMAR.
Firstly, write a value to
/sys/kernel/debug/iommu/intel/dmar_perf_latency
to enable sampling.
The possible values are as follows:
* 0 - disable sampling all latency data
* 1 - enable sampling IOTLB invalidation latency data
* 2 - enable sampling devTLB invalidation latency data
* 3 - enable sampling intr entry cache invalidation latency data
Next, read /sys/kernel/debug/iommu/intel/dmar_perf_latency gives
a snapshot of sampling result of all enabled monitors.
Examples in Kabylake:
::
1) Disable sampling all latency data:
$ sudo echo 0 > /sys/kernel/debug/iommu/intel/dmar_perf_latency
2) Enable sampling IOTLB invalidation latency data
$ sudo echo 1 > /sys/kernel/debug/iommu/intel/dmar_perf_latency
$ sudo cat /sys/kernel/debug/iommu/intel/dmar_perf_latency
IOMMU: dmar0 Register Base Address: 26be37000
<0.1us 0.1us-1us 1us-10us 10us-100us 100us-1ms
inv_iotlb 0 0 0 0 0
1ms-10ms >=10ms min(us) max(us) average(us)
inv_iotlb 0 0 0 0 0
[...]
IOMMU: dmar2 Register Base Address: fed91000
<0.1us 0.1us-1us 1us-10us 10us-100us 100us-1ms
inv_iotlb 0 0 18 0 0
1ms-10ms >=10ms min(us) max(us) average(us)
inv_iotlb 0 0 2 2 2
3) Enable sampling devTLB invalidation latency data
$ sudo echo 2 > /sys/kernel/debug/iommu/intel/dmar_perf_latency
$ sudo cat /sys/kernel/debug/iommu/intel/dmar_perf_latency
IOMMU: dmar0 Register Base Address: 26be37000
<0.1us 0.1us-1us 1us-10us 10us-100us 100us-1ms
inv_devtlb 0 0 0 0 0
>=10ms min(us) max(us) average(us)
inv_devtlb 0 0 0 0
[...]
What: /sys/kernel/debug/iommu/intel/<bdf>/domain_translation_struct
Date: December 2023
Contact: Jingqi Liu <Jingqi.liu@intel.com>
Description:
This file dumps a specified page table of Intel IOMMU
in legacy mode or scalable mode.
For a device that only supports legacy mode, dump its
page table by the debugfs file in the debugfs device
directory. e.g.
/sys/kernel/debug/iommu/intel/0000:00:02.0/domain_translation_struct.
For a device that supports scalable mode, dump the
page table of specified pasid by the debugfs file in
the debugfs pasid directory. e.g.
/sys/kernel/debug/iommu/intel/0000:00:02.0/1/domain_translation_struct.
Examples in Kabylake:
::
1) Dump the page table of device "0000:00:02.0" that only supports legacy mode.
$ sudo cat /sys/kernel/debug/iommu/intel/0000:00:02.0/domain_translation_struct
Device 0000:00:02.0 @0x1017f8000
IOVA_PFN PML5E PML4E
0x000000008d800 | 0x0000000000000000 0x00000001017f9003
0x000000008d801 | 0x0000000000000000 0x00000001017f9003
0x000000008d802 | 0x0000000000000000 0x00000001017f9003
PDPE PDE PTE
0x00000001017fa003 0x00000001017fb003 0x000000008d800003
0x00000001017fa003 0x00000001017fb003 0x000000008d801003
0x00000001017fa003 0x00000001017fb003 0x000000008d802003
[...]
2) Dump the page table of device "0000:00:0a.0" with PASID "1" that
supports scalable mode.
$ sudo cat /sys/kernel/debug/iommu/intel/0000:00:0a.0/1/domain_translation_struct
Device 0000:00:0a.0 with pasid 1 @0x10c112000
IOVA_PFN PML5E PML4E
0x0000000000000 | 0x0000000000000000 0x000000010df93003
0x0000000000001 | 0x0000000000000000 0x000000010df93003
0x0000000000002 | 0x0000000000000000 0x000000010df93003
PDPE PDE PTE
0x0000000106ae6003 0x0000000104b38003 0x0000000147c00803
0x0000000106ae6003 0x0000000104b38003 0x0000000147c01803
0x0000000106ae6003 0x0000000104b38003 0x0000000147c02803
[...]
...@@ -83,6 +83,7 @@ properties: ...@@ -83,6 +83,7 @@ properties:
- description: Qcom Adreno GPUs implementing "qcom,smmu-500" and "arm,mmu-500" - description: Qcom Adreno GPUs implementing "qcom,smmu-500" and "arm,mmu-500"
items: items:
- enum: - enum:
- qcom,qcm2290-smmu-500
- qcom,sa8775p-smmu-500 - qcom,sa8775p-smmu-500
- qcom,sc7280-smmu-500 - qcom,sc7280-smmu-500
- qcom,sc8280xp-smmu-500 - qcom,sc8280xp-smmu-500
...@@ -93,6 +94,7 @@ properties: ...@@ -93,6 +94,7 @@ properties:
- qcom,sm8350-smmu-500 - qcom,sm8350-smmu-500
- qcom,sm8450-smmu-500 - qcom,sm8450-smmu-500
- qcom,sm8550-smmu-500 - qcom,sm8550-smmu-500
- qcom,sm8650-smmu-500
- const: qcom,adreno-smmu - const: qcom,adreno-smmu
- const: qcom,smmu-500 - const: qcom,smmu-500
- const: arm,mmu-500 - const: arm,mmu-500
...@@ -462,6 +464,7 @@ allOf: ...@@ -462,6 +464,7 @@ allOf:
compatible: compatible:
items: items:
- enum: - enum:
- qcom,qcm2290-smmu-500
- qcom,sm6115-smmu-500 - qcom,sm6115-smmu-500
- qcom,sm6125-smmu-500 - qcom,sm6125-smmu-500
- const: qcom,adreno-smmu - const: qcom,adreno-smmu
...@@ -484,7 +487,12 @@ allOf: ...@@ -484,7 +487,12 @@ allOf:
- if: - if:
properties: properties:
compatible: compatible:
const: qcom,sm8450-smmu-500 items:
- const: qcom,sm8450-smmu-500
- const: qcom,adreno-smmu
- const: qcom,smmu-500
- const: arm,mmu-500
then: then:
properties: properties:
clock-names: clock-names:
...@@ -508,7 +516,13 @@ allOf: ...@@ -508,7 +516,13 @@ allOf:
- if: - if:
properties: properties:
compatible: compatible:
const: qcom,sm8550-smmu-500 items:
- enum:
- qcom,sm8550-smmu-500
- qcom,sm8650-smmu-500
- const: qcom,adreno-smmu
- const: qcom,smmu-500
- const: arm,mmu-500
then: then:
properties: properties:
clock-names: clock-names:
...@@ -534,7 +548,6 @@ allOf: ...@@ -534,7 +548,6 @@ allOf:
- cavium,smmu-v2 - cavium,smmu-v2
- marvell,ap806-smmu-500 - marvell,ap806-smmu-500
- nvidia,smmu-500 - nvidia,smmu-500
- qcom,qcm2290-smmu-500
- qcom,qdu1000-smmu-500 - qcom,qdu1000-smmu-500
- qcom,sc7180-smmu-500 - qcom,sc7180-smmu-500
- qcom,sc8180x-smmu-500 - qcom,sc8180x-smmu-500
...@@ -544,7 +557,6 @@ allOf: ...@@ -544,7 +557,6 @@ allOf:
- qcom,sdx65-smmu-500 - qcom,sdx65-smmu-500
- qcom,sm6350-smmu-500 - qcom,sm6350-smmu-500
- qcom,sm6375-smmu-500 - qcom,sm6375-smmu-500
- qcom,sm8650-smmu-500
- qcom,x1e80100-smmu-500 - qcom,x1e80100-smmu-500
then: then:
properties: properties:
......
...@@ -11283,7 +11283,6 @@ F: drivers/iommu/ ...@@ -11283,7 +11283,6 @@ F: drivers/iommu/
F: include/linux/iommu.h F: include/linux/iommu.h
F: include/linux/iova.h F: include/linux/iova.h
F: include/linux/of_iommu.h F: include/linux/of_iommu.h
F: include/uapi/linux/iommu.h
IOMMUFD IOMMUFD
M: Jason Gunthorpe <jgg@nvidia.com> M: Jason Gunthorpe <jgg@nvidia.com>
......
...@@ -163,6 +163,9 @@ config IOMMU_SVA ...@@ -163,6 +163,9 @@ config IOMMU_SVA
select IOMMU_MM_DATA select IOMMU_MM_DATA
bool bool
config IOMMU_IOPF
bool
config FSL_PAMU config FSL_PAMU
bool "Freescale IOMMU support" bool "Freescale IOMMU support"
depends on PCI depends on PCI
...@@ -196,7 +199,7 @@ source "drivers/iommu/iommufd/Kconfig" ...@@ -196,7 +199,7 @@ source "drivers/iommu/iommufd/Kconfig"
config IRQ_REMAP config IRQ_REMAP
bool "Support for Interrupt Remapping" bool "Support for Interrupt Remapping"
depends on X86_64 && X86_IO_APIC && PCI_MSI && ACPI depends on X86_64 && X86_IO_APIC && PCI_MSI && ACPI
select DMAR_TABLE select DMAR_TABLE if INTEL_IOMMU
help help
Supports Interrupt remapping for IO-APIC and MSI devices. Supports Interrupt remapping for IO-APIC and MSI devices.
To use x2apic mode in the CPU's which support x2APIC enhancements or To use x2apic mode in the CPU's which support x2APIC enhancements or
...@@ -398,6 +401,7 @@ config ARM_SMMU_V3_SVA ...@@ -398,6 +401,7 @@ config ARM_SMMU_V3_SVA
bool "Shared Virtual Addressing support for the ARM SMMUv3" bool "Shared Virtual Addressing support for the ARM SMMUv3"
depends on ARM_SMMU_V3 depends on ARM_SMMU_V3
select IOMMU_SVA select IOMMU_SVA
select IOMMU_IOPF
select MMU_NOTIFIER select MMU_NOTIFIER
help help
Support for sharing process address spaces with devices using the Support for sharing process address spaces with devices using the
......
...@@ -26,6 +26,7 @@ obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o ...@@ -26,6 +26,7 @@ obj-$(CONFIG_FSL_PAMU) += fsl_pamu.o fsl_pamu_domain.o
obj-$(CONFIG_S390_IOMMU) += s390-iommu.o obj-$(CONFIG_S390_IOMMU) += s390-iommu.o
obj-$(CONFIG_HYPERV_IOMMU) += hyperv-iommu.o obj-$(CONFIG_HYPERV_IOMMU) += hyperv-iommu.o
obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o obj-$(CONFIG_VIRTIO_IOMMU) += virtio-iommu.o
obj-$(CONFIG_IOMMU_SVA) += iommu-sva.o io-pgfault.o obj-$(CONFIG_IOMMU_SVA) += iommu-sva.o
obj-$(CONFIG_IOMMU_IOPF) += io-pgfault.o
obj-$(CONFIG_SPRD_IOMMU) += sprd-iommu.o obj-$(CONFIG_SPRD_IOMMU) += sprd-iommu.o
obj-$(CONFIG_APPLE_DART) += apple-dart.o obj-$(CONFIG_APPLE_DART) += apple-dart.o
...@@ -39,20 +39,16 @@ extern enum io_pgtable_fmt amd_iommu_pgtable; ...@@ -39,20 +39,16 @@ extern enum io_pgtable_fmt amd_iommu_pgtable;
extern int amd_iommu_gpt_level; extern int amd_iommu_gpt_level;
bool amd_iommu_v2_supported(void); bool amd_iommu_v2_supported(void);
struct amd_iommu *get_amd_iommu(unsigned int idx);
u8 amd_iommu_pc_get_max_banks(unsigned int idx);
bool amd_iommu_pc_supported(void);
u8 amd_iommu_pc_get_max_counters(unsigned int idx);
int amd_iommu_pc_get_reg(struct amd_iommu *iommu, u8 bank, u8 cntr,
u8 fxn, u64 *value);
int amd_iommu_pc_set_reg(struct amd_iommu *iommu, u8 bank, u8 cntr,
u8 fxn, u64 *value);
/* Device capabilities */ /* Device capabilities */
int amd_iommu_pdev_enable_cap_pri(struct pci_dev *pdev); int amd_iommu_pdev_enable_cap_pri(struct pci_dev *pdev);
void amd_iommu_pdev_disable_cap_pri(struct pci_dev *pdev); void amd_iommu_pdev_disable_cap_pri(struct pci_dev *pdev);
int amd_iommu_flush_page(struct iommu_domain *dom, u32 pasid, u64 address); /* GCR3 setup */
int amd_iommu_set_gcr3(struct iommu_dev_data *dev_data,
ioasid_t pasid, unsigned long gcr3);
int amd_iommu_clear_gcr3(struct iommu_dev_data *dev_data, ioasid_t pasid);
/* /*
* This function flushes all internal caches of * This function flushes all internal caches of
* the IOMMU used by this driver. * the IOMMU used by this driver.
...@@ -63,10 +59,10 @@ void amd_iommu_domain_update(struct protection_domain *domain); ...@@ -63,10 +59,10 @@ void amd_iommu_domain_update(struct protection_domain *domain);
void amd_iommu_domain_flush_complete(struct protection_domain *domain); void amd_iommu_domain_flush_complete(struct protection_domain *domain);
void amd_iommu_domain_flush_pages(struct protection_domain *domain, void amd_iommu_domain_flush_pages(struct protection_domain *domain,
u64 address, size_t size); u64 address, size_t size);
int amd_iommu_flush_tlb(struct iommu_domain *dom, u32 pasid); void amd_iommu_dev_flush_pasid_pages(struct iommu_dev_data *dev_data,
int amd_iommu_domain_set_gcr3(struct iommu_domain *dom, u32 pasid, ioasid_t pasid, u64 address, size_t size);
unsigned long cr3); void amd_iommu_dev_flush_pasid_all(struct iommu_dev_data *dev_data,
int amd_iommu_domain_clear_gcr3(struct iommu_domain *dom, u32 pasid); ioasid_t pasid);
#ifdef CONFIG_IRQ_REMAP #ifdef CONFIG_IRQ_REMAP
int amd_iommu_create_irq_domain(struct amd_iommu *iommu); int amd_iommu_create_irq_domain(struct amd_iommu *iommu);
...@@ -77,10 +73,6 @@ static inline int amd_iommu_create_irq_domain(struct amd_iommu *iommu) ...@@ -77,10 +73,6 @@ static inline int amd_iommu_create_irq_domain(struct amd_iommu *iommu)
} }
#endif #endif
#define PPR_SUCCESS 0x0
#define PPR_INVALID 0x1
#define PPR_FAILURE 0xf
int amd_iommu_complete_ppr(struct pci_dev *pdev, u32 pasid, int amd_iommu_complete_ppr(struct pci_dev *pdev, u32 pasid,
int status, int tag); int status, int tag);
...@@ -150,6 +142,21 @@ static inline void *alloc_pgtable_page(int nid, gfp_t gfp) ...@@ -150,6 +142,21 @@ static inline void *alloc_pgtable_page(int nid, gfp_t gfp)
return page ? page_address(page) : NULL; return page ? page_address(page) : NULL;
} }
/*
* This must be called after device probe completes. During probe
* use rlookup_amd_iommu() get the iommu.
*/
static inline struct amd_iommu *get_amd_iommu_from_dev(struct device *dev)
{
return iommu_get_iommu_dev(dev, struct amd_iommu, iommu);
}
/* This must be called after device probe completes. */
static inline struct amd_iommu *get_amd_iommu_from_dev_data(struct iommu_dev_data *dev_data)
{
return iommu_get_iommu_dev(dev_data->dev, struct amd_iommu, iommu);
}
bool translation_pre_enabled(struct amd_iommu *iommu); bool translation_pre_enabled(struct amd_iommu *iommu);
bool amd_iommu_is_attach_deferred(struct device *dev); bool amd_iommu_is_attach_deferred(struct device *dev);
int __init add_special_device(u8 type, u8 id, u32 *devid, bool cmd_line); int __init add_special_device(u8 type, u8 id, u32 *devid, bool cmd_line);
......
...@@ -453,15 +453,6 @@ ...@@ -453,15 +453,6 @@
#define MAX_DOMAIN_ID 65536 #define MAX_DOMAIN_ID 65536
/* Protection domain flags */
#define PD_DMA_OPS_MASK BIT(0) /* domain used for dma_ops */
#define PD_DEFAULT_MASK BIT(1) /* domain is a default dma_ops
domain for an IOMMU */
#define PD_PASSTHROUGH_MASK BIT(2) /* domain has no page
translation */
#define PD_IOMMUV2_MASK BIT(3) /* domain has gcr3 table */
#define PD_GIOV_MASK BIT(4) /* domain enable GIOV support */
/* Timeout stuff */ /* Timeout stuff */
#define LOOP_TIMEOUT 100000 #define LOOP_TIMEOUT 100000
#define MMIO_STATUS_TIMEOUT 2000000 #define MMIO_STATUS_TIMEOUT 2000000
...@@ -513,14 +504,6 @@ extern struct kmem_cache *amd_iommu_irq_cache; ...@@ -513,14 +504,6 @@ extern struct kmem_cache *amd_iommu_irq_cache;
#define for_each_iommu_safe(iommu, next) \ #define for_each_iommu_safe(iommu, next) \
list_for_each_entry_safe((iommu), (next), &amd_iommu_list, list) list_for_each_entry_safe((iommu), (next), &amd_iommu_list, list)
#define APERTURE_RANGE_SHIFT 27 /* 128 MB */
#define APERTURE_RANGE_SIZE (1ULL << APERTURE_RANGE_SHIFT)
#define APERTURE_RANGE_PAGES (APERTURE_RANGE_SIZE >> PAGE_SHIFT)
#define APERTURE_MAX_RANGES 32 /* allows 4GB of DMA address space */
#define APERTURE_RANGE_INDEX(a) ((a) >> APERTURE_RANGE_SHIFT)
#define APERTURE_PAGE_INDEX(a) (((a) >> 21) & 0x3fULL)
struct amd_iommu; struct amd_iommu;
struct iommu_domain; struct iommu_domain;
struct irq_domain; struct irq_domain;
...@@ -541,6 +524,13 @@ struct amd_irte_ops; ...@@ -541,6 +524,13 @@ struct amd_irte_ops;
#define io_pgtable_cfg_to_data(x) \ #define io_pgtable_cfg_to_data(x) \
container_of((x), struct amd_io_pgtable, pgtbl_cfg) container_of((x), struct amd_io_pgtable, pgtbl_cfg)
struct gcr3_tbl_info {
u64 *gcr3_tbl; /* Guest CR3 table */
int glx; /* Number of levels for GCR3 table */
u32 pasid_cnt; /* Track attached PASIDs */
u16 domid; /* Per device domain ID */
};
struct amd_io_pgtable { struct amd_io_pgtable {
struct io_pgtable_cfg pgtbl_cfg; struct io_pgtable_cfg pgtbl_cfg;
struct io_pgtable iop; struct io_pgtable iop;
...@@ -549,6 +539,11 @@ struct amd_io_pgtable { ...@@ -549,6 +539,11 @@ struct amd_io_pgtable {
u64 *pgd; /* v2 pgtable pgd pointer */ u64 *pgd; /* v2 pgtable pgd pointer */
}; };
enum protection_domain_mode {
PD_MODE_V1 = 1,
PD_MODE_V2,
};
/* /*
* This structure contains generic data for IOMMU protection domains * This structure contains generic data for IOMMU protection domains
* independent of their use. * independent of their use.
...@@ -560,10 +555,8 @@ struct protection_domain { ...@@ -560,10 +555,8 @@ struct protection_domain {
struct amd_io_pgtable iop; struct amd_io_pgtable iop;
spinlock_t lock; /* mostly used to lock the page table*/ spinlock_t lock; /* mostly used to lock the page table*/
u16 id; /* the domain id written to the device table */ u16 id; /* the domain id written to the device table */
int glx; /* Number of levels for GCR3 table */
int nid; /* Node ID */ int nid; /* Node ID */
u64 *gcr3_tbl; /* Guest CR3 table */ enum protection_domain_mode pd_mode; /* Track page table type */
unsigned long flags; /* flags to find out type of domain */
bool dirty_tracking; /* dirty tracking is enabled in the domain */ bool dirty_tracking; /* dirty tracking is enabled in the domain */
unsigned dev_cnt; /* devices assigned to this domain */ unsigned dev_cnt; /* devices assigned to this domain */
unsigned dev_iommu[MAX_IOMMUS]; /* per-IOMMU reference count */ unsigned dev_iommu[MAX_IOMMUS]; /* per-IOMMU reference count */
...@@ -816,6 +809,7 @@ struct iommu_dev_data { ...@@ -816,6 +809,7 @@ struct iommu_dev_data {
struct list_head list; /* For domain->dev_list */ struct list_head list; /* For domain->dev_list */
struct llist_node dev_data_list; /* For global dev_data_list */ struct llist_node dev_data_list; /* For global dev_data_list */
struct protection_domain *domain; /* Domain the device is bound to */ struct protection_domain *domain; /* Domain the device is bound to */
struct gcr3_tbl_info gcr3_info; /* Per-device GCR3 table */
struct device *dev; struct device *dev;
u16 devid; /* PCI Device ID */ u16 devid; /* PCI Device ID */
......
...@@ -2069,6 +2069,9 @@ static int __init iommu_init_pci(struct amd_iommu *iommu) ...@@ -2069,6 +2069,9 @@ static int __init iommu_init_pci(struct amd_iommu *iommu)
/* Prevent binding other PCI device drivers to IOMMU devices */ /* Prevent binding other PCI device drivers to IOMMU devices */
iommu->dev->match_driver = false; iommu->dev->match_driver = false;
/* ACPI _PRT won't have an IRQ for IOMMU */
iommu->dev->irq_managed = 1;
pci_read_config_dword(iommu->dev, cap_ptr + MMIO_CAP_HDR_OFFSET, pci_read_config_dword(iommu->dev, cap_ptr + MMIO_CAP_HDR_OFFSET,
&iommu->cap); &iommu->cap);
...@@ -2770,6 +2773,7 @@ static void early_enable_iommu(struct amd_iommu *iommu) ...@@ -2770,6 +2773,7 @@ static void early_enable_iommu(struct amd_iommu *iommu)
iommu_enable_command_buffer(iommu); iommu_enable_command_buffer(iommu);
iommu_enable_event_buffer(iommu); iommu_enable_event_buffer(iommu);
iommu_set_exclusion_range(iommu); iommu_set_exclusion_range(iommu);
iommu_enable_gt(iommu);
iommu_enable_ga(iommu); iommu_enable_ga(iommu);
iommu_enable_xt(iommu); iommu_enable_xt(iommu);
iommu_enable_irtcachedis(iommu); iommu_enable_irtcachedis(iommu);
...@@ -2826,6 +2830,7 @@ static void early_enable_iommus(void) ...@@ -2826,6 +2830,7 @@ static void early_enable_iommus(void)
iommu_disable_irtcachedis(iommu); iommu_disable_irtcachedis(iommu);
iommu_enable_command_buffer(iommu); iommu_enable_command_buffer(iommu);
iommu_enable_event_buffer(iommu); iommu_enable_event_buffer(iommu);
iommu_enable_gt(iommu);
iommu_enable_ga(iommu); iommu_enable_ga(iommu);
iommu_enable_xt(iommu); iommu_enable_xt(iommu);
iommu_enable_irtcachedis(iommu); iommu_enable_irtcachedis(iommu);
...@@ -2839,10 +2844,8 @@ static void enable_iommus_v2(void) ...@@ -2839,10 +2844,8 @@ static void enable_iommus_v2(void)
{ {
struct amd_iommu *iommu; struct amd_iommu *iommu;
for_each_iommu(iommu) { for_each_iommu(iommu)
iommu_enable_ppr_log(iommu); iommu_enable_ppr_log(iommu);
iommu_enable_gt(iommu);
}
} }
static void enable_iommus_vapic(void) static void enable_iommus_vapic(void)
...@@ -3726,13 +3729,11 @@ u8 amd_iommu_pc_get_max_banks(unsigned int idx) ...@@ -3726,13 +3729,11 @@ u8 amd_iommu_pc_get_max_banks(unsigned int idx)
return 0; return 0;
} }
EXPORT_SYMBOL(amd_iommu_pc_get_max_banks);
bool amd_iommu_pc_supported(void) bool amd_iommu_pc_supported(void)
{ {
return amd_iommu_pc_present; return amd_iommu_pc_present;
} }
EXPORT_SYMBOL(amd_iommu_pc_supported);
u8 amd_iommu_pc_get_max_counters(unsigned int idx) u8 amd_iommu_pc_get_max_counters(unsigned int idx)
{ {
...@@ -3743,7 +3744,6 @@ u8 amd_iommu_pc_get_max_counters(unsigned int idx) ...@@ -3743,7 +3744,6 @@ u8 amd_iommu_pc_get_max_counters(unsigned int idx)
return 0; return 0;
} }
EXPORT_SYMBOL(amd_iommu_pc_get_max_counters);
static int iommu_pc_get_set_reg(struct amd_iommu *iommu, u8 bank, u8 cntr, static int iommu_pc_get_set_reg(struct amd_iommu *iommu, u8 bank, u8 cntr,
u8 fxn, u64 *value, bool is_write) u8 fxn, u64 *value, bool is_write)
......
...@@ -350,38 +350,26 @@ static const struct iommu_flush_ops v2_flush_ops = { ...@@ -350,38 +350,26 @@ static const struct iommu_flush_ops v2_flush_ops = {
static void v2_free_pgtable(struct io_pgtable *iop) static void v2_free_pgtable(struct io_pgtable *iop)
{ {
struct protection_domain *pdom;
struct amd_io_pgtable *pgtable = container_of(iop, struct amd_io_pgtable, iop); struct amd_io_pgtable *pgtable = container_of(iop, struct amd_io_pgtable, iop);
pdom = container_of(pgtable, struct protection_domain, iop); if (!pgtable || !pgtable->pgd)
if (!(pdom->flags & PD_IOMMUV2_MASK))
return; return;
/* Clear gcr3 entry */
amd_iommu_domain_clear_gcr3(&pdom->domain, 0);
/* Make changes visible to IOMMUs */
amd_iommu_domain_update(pdom);
/* Free page table */ /* Free page table */
free_pgtable(pgtable->pgd, get_pgtable_level()); free_pgtable(pgtable->pgd, get_pgtable_level());
pgtable->pgd = NULL;
} }
static struct io_pgtable *v2_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie) static struct io_pgtable *v2_alloc_pgtable(struct io_pgtable_cfg *cfg, void *cookie)
{ {
struct amd_io_pgtable *pgtable = io_pgtable_cfg_to_data(cfg); struct amd_io_pgtable *pgtable = io_pgtable_cfg_to_data(cfg);
struct protection_domain *pdom = (struct protection_domain *)cookie; struct protection_domain *pdom = (struct protection_domain *)cookie;
int ret;
int ias = IOMMU_IN_ADDR_BIT_SIZE; int ias = IOMMU_IN_ADDR_BIT_SIZE;
pgtable->pgd = alloc_pgtable_page(pdom->nid, GFP_ATOMIC); pgtable->pgd = alloc_pgtable_page(pdom->nid, GFP_ATOMIC);
if (!pgtable->pgd) if (!pgtable->pgd)
return NULL; return NULL;
ret = amd_iommu_domain_set_gcr3(&pdom->domain, 0, iommu_virt_to_phys(pgtable->pgd));
if (ret)
goto err_free_pgd;
if (get_pgtable_level() == PAGE_MODE_5_LEVEL) if (get_pgtable_level() == PAGE_MODE_5_LEVEL)
ias = 57; ias = 57;
...@@ -395,11 +383,6 @@ static struct io_pgtable *v2_alloc_pgtable(struct io_pgtable_cfg *cfg, void *coo ...@@ -395,11 +383,6 @@ static struct io_pgtable *v2_alloc_pgtable(struct io_pgtable_cfg *cfg, void *coo
cfg->tlb = &v2_flush_ops; cfg->tlb = &v2_flush_ops;
return &pgtable->iop; return &pgtable->iop;
err_free_pgd:
free_pgtable_page(pgtable->pgd);
return NULL;
} }
struct io_pgtable_init_fns io_pgtable_amd_iommu_v2_init_fns = { struct io_pgtable_init_fns io_pgtable_amd_iommu_v2_init_fns = {
......
This diff is collapsed.
...@@ -779,7 +779,8 @@ static void apple_dart_domain_free(struct iommu_domain *domain) ...@@ -779,7 +779,8 @@ static void apple_dart_domain_free(struct iommu_domain *domain)
kfree(dart_domain); kfree(dart_domain);
} }
static int apple_dart_of_xlate(struct device *dev, struct of_phandle_args *args) static int apple_dart_of_xlate(struct device *dev,
const struct of_phandle_args *args)
{ {
struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev); struct apple_dart_master_cfg *cfg = dev_iommu_priv_get(dev);
struct platform_device *iommu_pdev = of_find_device_by_node(args->np); struct platform_device *iommu_pdev = of_find_device_by_node(args->np);
......
...@@ -10,7 +10,6 @@ ...@@ -10,7 +10,6 @@
#include <linux/slab.h> #include <linux/slab.h>
#include "arm-smmu-v3.h" #include "arm-smmu-v3.h"
#include "../../iommu-sva.h"
#include "../../io-pgtable-arm.h" #include "../../io-pgtable-arm.h"
struct arm_smmu_mmu_notifier { struct arm_smmu_mmu_notifier {
...@@ -364,7 +363,13 @@ static int __arm_smmu_sva_bind(struct device *dev, ioasid_t pasid, ...@@ -364,7 +363,13 @@ static int __arm_smmu_sva_bind(struct device *dev, ioasid_t pasid,
struct arm_smmu_bond *bond; struct arm_smmu_bond *bond;
struct arm_smmu_master *master = dev_iommu_priv_get(dev); struct arm_smmu_master *master = dev_iommu_priv_get(dev);
struct iommu_domain *domain = iommu_get_domain_for_dev(dev); struct iommu_domain *domain = iommu_get_domain_for_dev(dev);
struct arm_smmu_domain *smmu_domain = to_smmu_domain(domain); struct arm_smmu_domain *smmu_domain;
if (!(domain->type & __IOMMU_DOMAIN_PAGING))
return -ENODEV;
smmu_domain = to_smmu_domain(domain);
if (smmu_domain->stage != ARM_SMMU_DOMAIN_S1)
return -ENODEV;
if (!master || !master->sva_enabled) if (!master || !master->sva_enabled)
return -ENODEV; return -ENODEV;
...@@ -470,7 +475,6 @@ bool arm_smmu_master_sva_enabled(struct arm_smmu_master *master) ...@@ -470,7 +475,6 @@ bool arm_smmu_master_sva_enabled(struct arm_smmu_master *master)
static int arm_smmu_master_sva_enable_iopf(struct arm_smmu_master *master) static int arm_smmu_master_sva_enable_iopf(struct arm_smmu_master *master)
{ {
int ret;
struct device *dev = master->dev; struct device *dev = master->dev;
/* /*
...@@ -483,16 +487,7 @@ static int arm_smmu_master_sva_enable_iopf(struct arm_smmu_master *master) ...@@ -483,16 +487,7 @@ static int arm_smmu_master_sva_enable_iopf(struct arm_smmu_master *master)
if (!master->iopf_enabled) if (!master->iopf_enabled)
return -EINVAL; return -EINVAL;
ret = iopf_queue_add_device(master->smmu->evtq.iopf, dev); return iopf_queue_add_device(master->smmu->evtq.iopf, dev);
if (ret)
return ret;
ret = iommu_register_device_fault_handler(dev, iommu_queue_iopf, dev);
if (ret) {
iopf_queue_remove_device(master->smmu->evtq.iopf, dev);
return ret;
}
return 0;
} }
static void arm_smmu_master_sva_disable_iopf(struct arm_smmu_master *master) static void arm_smmu_master_sva_disable_iopf(struct arm_smmu_master *master)
...@@ -502,7 +497,6 @@ static void arm_smmu_master_sva_disable_iopf(struct arm_smmu_master *master) ...@@ -502,7 +497,6 @@ static void arm_smmu_master_sva_disable_iopf(struct arm_smmu_master *master)
if (!master->iopf_enabled) if (!master->iopf_enabled)
return; return;
iommu_unregister_device_fault_handler(dev);
iopf_queue_remove_device(master->smmu->evtq.iopf, dev); iopf_queue_remove_device(master->smmu->evtq.iopf, dev);
} }
......
This diff is collapsed.
...@@ -609,8 +609,6 @@ struct arm_smmu_ctx_desc_cfg { ...@@ -609,8 +609,6 @@ struct arm_smmu_ctx_desc_cfg {
struct arm_smmu_s2_cfg { struct arm_smmu_s2_cfg {
u16 vmid; u16 vmid;
u64 vttbr;
u64 vtcr;
}; };
struct arm_smmu_strtab_cfg { struct arm_smmu_strtab_cfg {
...@@ -697,7 +695,6 @@ struct arm_smmu_stream { ...@@ -697,7 +695,6 @@ struct arm_smmu_stream {
struct arm_smmu_master { struct arm_smmu_master {
struct arm_smmu_device *smmu; struct arm_smmu_device *smmu;
struct device *dev; struct device *dev;
struct arm_smmu_domain *domain;
struct list_head domain_head; struct list_head domain_head;
struct arm_smmu_stream *streams; struct arm_smmu_stream *streams;
/* Locked by the iommu core using the group mutex */ /* Locked by the iommu core using the group mutex */
...@@ -715,7 +712,6 @@ struct arm_smmu_master { ...@@ -715,7 +712,6 @@ struct arm_smmu_master {
enum arm_smmu_domain_stage { enum arm_smmu_domain_stage {
ARM_SMMU_DOMAIN_S1 = 0, ARM_SMMU_DOMAIN_S1 = 0,
ARM_SMMU_DOMAIN_S2, ARM_SMMU_DOMAIN_S2,
ARM_SMMU_DOMAIN_BYPASS,
}; };
struct arm_smmu_domain { struct arm_smmu_domain {
......
...@@ -260,6 +260,7 @@ static const struct of_device_id qcom_smmu_client_of_match[] __maybe_unused = { ...@@ -260,6 +260,7 @@ static const struct of_device_id qcom_smmu_client_of_match[] __maybe_unused = {
{ .compatible = "qcom,sm6375-mdss" }, { .compatible = "qcom,sm6375-mdss" },
{ .compatible = "qcom,sm8150-mdss" }, { .compatible = "qcom,sm8150-mdss" },
{ .compatible = "qcom,sm8250-mdss" }, { .compatible = "qcom,sm8250-mdss" },
{ .compatible = "qcom,x1e80100-mdss" },
{ } { }
}; };
......
...@@ -1546,7 +1546,8 @@ static int arm_smmu_set_pgtable_quirks(struct iommu_domain *domain, ...@@ -1546,7 +1546,8 @@ static int arm_smmu_set_pgtable_quirks(struct iommu_domain *domain,
return ret; return ret;
} }
static int arm_smmu_of_xlate(struct device *dev, struct of_phandle_args *args) static int arm_smmu_of_xlate(struct device *dev,
const struct of_phandle_args *args)
{ {
u32 mask, fwid = 0; u32 mask, fwid = 0;
......
...@@ -546,7 +546,8 @@ static struct iommu_device *qcom_iommu_probe_device(struct device *dev) ...@@ -546,7 +546,8 @@ static struct iommu_device *qcom_iommu_probe_device(struct device *dev)
return &qcom_iommu->iommu; return &qcom_iommu->iommu;
} }
static int qcom_iommu_of_xlate(struct device *dev, struct of_phandle_args *args) static int qcom_iommu_of_xlate(struct device *dev,
const struct of_phandle_args *args)
{ {
struct qcom_iommu_dev *qcom_iommu; struct qcom_iommu_dev *qcom_iommu;
struct platform_device *iommu_pdev; struct platform_device *iommu_pdev;
......
...@@ -859,6 +859,11 @@ static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys, ...@@ -859,6 +859,11 @@ static dma_addr_t __iommu_dma_map(struct device *dev, phys_addr_t phys,
iommu_deferred_attach(dev, domain)) iommu_deferred_attach(dev, domain))
return DMA_MAPPING_ERROR; return DMA_MAPPING_ERROR;
/* If anyone ever wants this we'd need support in the IOVA allocator */
if (dev_WARN_ONCE(dev, dma_get_min_align_mask(dev) > iova_mask(iovad),
"Unsupported alignment constraint\n"))
return DMA_MAPPING_ERROR;
size = iova_align(iovad, size + iova_off); size = iova_align(iovad, size + iova_off);
iova = iommu_dma_alloc_iova(domain, size, dma_mask, dev); iova = iommu_dma_alloc_iova(domain, size, dma_mask, dev);
......
...@@ -1431,7 +1431,7 @@ static void exynos_iommu_release_device(struct device *dev) ...@@ -1431,7 +1431,7 @@ static void exynos_iommu_release_device(struct device *dev)
} }
static int exynos_iommu_of_xlate(struct device *dev, static int exynos_iommu_of_xlate(struct device *dev,
struct of_phandle_args *spec) const struct of_phandle_args *spec)
{ {
struct platform_device *sysmmu = of_find_device_by_node(spec->np); struct platform_device *sysmmu = of_find_device_by_node(spec->np);
struct exynos_iommu_owner *owner = dev_iommu_priv_get(dev); struct exynos_iommu_owner *owner = dev_iommu_priv_get(dev);
......
...@@ -51,6 +51,7 @@ config INTEL_IOMMU_SVM ...@@ -51,6 +51,7 @@ config INTEL_IOMMU_SVM
depends on X86_64 depends on X86_64
select MMU_NOTIFIER select MMU_NOTIFIER
select IOMMU_SVA select IOMMU_SVA
select IOMMU_IOPF
help help
Shared Virtual Memory (SVM) provides a facility for devices Shared Virtual Memory (SVM) provides a facility for devices
to access DMA resources through process address space by to access DMA resources through process address space by
...@@ -64,17 +65,6 @@ config INTEL_IOMMU_DEFAULT_ON ...@@ -64,17 +65,6 @@ config INTEL_IOMMU_DEFAULT_ON
one is found. If this option is not selected, DMAR support can one is found. If this option is not selected, DMAR support can
be enabled by passing intel_iommu=on to the kernel. be enabled by passing intel_iommu=on to the kernel.
config INTEL_IOMMU_BROKEN_GFX_WA
bool "Workaround broken graphics drivers (going away soon)"
depends on BROKEN && X86
help
Current Graphics drivers tend to use physical address
for DMA and avoid using DMA APIs. Setting this config
option permits the IOMMU driver to set a unity map for
all the OS-visible memory. Hence the driver can continue
to use physical addresses for DMA, at least until this
option is removed in the 2.6.32 kernel.
config INTEL_IOMMU_FLOPPY_WA config INTEL_IOMMU_FLOPPY_WA
def_bool y def_bool y
depends on X86 depends on X86
......
...@@ -5,5 +5,7 @@ obj-$(CONFIG_DMAR_TABLE) += trace.o cap_audit.o ...@@ -5,5 +5,7 @@ obj-$(CONFIG_DMAR_TABLE) += trace.o cap_audit.o
obj-$(CONFIG_DMAR_PERF) += perf.o obj-$(CONFIG_DMAR_PERF) += perf.o
obj-$(CONFIG_INTEL_IOMMU_DEBUGFS) += debugfs.o obj-$(CONFIG_INTEL_IOMMU_DEBUGFS) += debugfs.o
obj-$(CONFIG_INTEL_IOMMU_SVM) += svm.o obj-$(CONFIG_INTEL_IOMMU_SVM) += svm.o
ifdef CONFIG_INTEL_IOMMU
obj-$(CONFIG_IRQ_REMAP) += irq_remapping.o obj-$(CONFIG_IRQ_REMAP) += irq_remapping.o
endif
obj-$(CONFIG_INTEL_IOMMU_PERF_EVENTS) += perfmon.o obj-$(CONFIG_INTEL_IOMMU_PERF_EVENTS) += perfmon.o
...@@ -1095,7 +1095,9 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd) ...@@ -1095,7 +1095,9 @@ static int alloc_iommu(struct dmar_drhd_unit *drhd)
iommu->agaw = agaw; iommu->agaw = agaw;
iommu->msagaw = msagaw; iommu->msagaw = msagaw;
iommu->segment = drhd->segment; iommu->segment = drhd->segment;
iommu->device_rbtree = RB_ROOT;
spin_lock_init(&iommu->device_rbtree_lock);
mutex_init(&iommu->iopf_lock);
iommu->node = NUMA_NO_NODE; iommu->node = NUMA_NO_NODE;
ver = readl(iommu->reg + DMAR_VER_REG); ver = readl(iommu->reg + DMAR_VER_REG);
...@@ -1271,6 +1273,8 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index) ...@@ -1271,6 +1273,8 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index)
{ {
u32 fault; u32 fault;
int head, tail; int head, tail;
struct device *dev;
u64 iqe_err, ite_sid;
struct q_inval *qi = iommu->qi; struct q_inval *qi = iommu->qi;
int shift = qi_shift(iommu); int shift = qi_shift(iommu);
...@@ -1315,6 +1319,13 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index) ...@@ -1315,6 +1319,13 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index)
tail = readl(iommu->reg + DMAR_IQT_REG); tail = readl(iommu->reg + DMAR_IQT_REG);
tail = ((tail >> shift) - 1 + QI_LENGTH) % QI_LENGTH; tail = ((tail >> shift) - 1 + QI_LENGTH) % QI_LENGTH;
/*
* SID field is valid only when the ITE field is Set in FSTS_REG
* see Intel VT-d spec r4.1, section 11.4.9.9
*/
iqe_err = dmar_readq(iommu->reg + DMAR_IQER_REG);
ite_sid = DMAR_IQER_REG_ITESID(iqe_err);
writel(DMA_FSTS_ITE, iommu->reg + DMAR_FSTS_REG); writel(DMA_FSTS_ITE, iommu->reg + DMAR_FSTS_REG);
pr_info("Invalidation Time-out Error (ITE) cleared\n"); pr_info("Invalidation Time-out Error (ITE) cleared\n");
...@@ -1324,6 +1335,19 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index) ...@@ -1324,6 +1335,19 @@ static int qi_check_fault(struct intel_iommu *iommu, int index, int wait_index)
head = (head - 2 + QI_LENGTH) % QI_LENGTH; head = (head - 2 + QI_LENGTH) % QI_LENGTH;
} while (head != tail); } while (head != tail);
/*
* If device was released or isn't present, no need to retry
* the ATS invalidate request anymore.
*
* 0 value of ite_sid means old VT-d device, no ite_sid value.
* see Intel VT-d spec r4.1, section 11.4.9.9
*/
if (ite_sid) {
dev = device_rbtree_find(iommu, ite_sid);
if (!dev || !dev_is_pci(dev) ||
!pci_device_is_present(to_pci_dev(dev)))
return -ETIMEDOUT;
}
if (qi->desc_status[wait_index] == QI_ABORT) if (qi->desc_status[wait_index] == QI_ABORT)
return -EAGAIN; return -EAGAIN;
} }
......
This diff is collapsed.
...@@ -719,9 +719,16 @@ struct intel_iommu { ...@@ -719,9 +719,16 @@ struct intel_iommu {
#endif #endif
struct iopf_queue *iopf_queue; struct iopf_queue *iopf_queue;
unsigned char iopfq_name[16]; unsigned char iopfq_name[16];
/* Synchronization between fault report and iommu device release. */
struct mutex iopf_lock;
struct q_inval *qi; /* Queued invalidation info */ struct q_inval *qi; /* Queued invalidation info */
u32 iommu_state[MAX_SR_DMAR_REGS]; /* Store iommu states between suspend and resume.*/ u32 iommu_state[MAX_SR_DMAR_REGS]; /* Store iommu states between suspend and resume.*/
/* rb tree for all probed devices */
struct rb_root device_rbtree;
/* protect the device_rbtree */
spinlock_t device_rbtree_lock;
#ifdef CONFIG_IRQ_REMAP #ifdef CONFIG_IRQ_REMAP
struct ir_table *ir_table; /* Interrupt remapping info */ struct ir_table *ir_table; /* Interrupt remapping info */
struct irq_domain *ir_domain; struct irq_domain *ir_domain;
...@@ -755,6 +762,8 @@ struct device_domain_info { ...@@ -755,6 +762,8 @@ struct device_domain_info {
struct intel_iommu *iommu; /* IOMMU used by this device */ struct intel_iommu *iommu; /* IOMMU used by this device */
struct dmar_domain *domain; /* pointer to domain */ struct dmar_domain *domain; /* pointer to domain */
struct pasid_table *pasid_table; /* pasid table */ struct pasid_table *pasid_table; /* pasid table */
/* device tracking node(lookup by PCI RID) */
struct rb_node node;
#ifdef CONFIG_INTEL_IOMMU_DEBUGFS #ifdef CONFIG_INTEL_IOMMU_DEBUGFS
struct dentry *debugfs_dentry; /* pointer to device directory dentry */ struct dentry *debugfs_dentry; /* pointer to device directory dentry */
#endif #endif
...@@ -1081,12 +1090,13 @@ void free_pgtable_page(void *vaddr); ...@@ -1081,12 +1090,13 @@ void free_pgtable_page(void *vaddr);
void iommu_flush_write_buffer(struct intel_iommu *iommu); void iommu_flush_write_buffer(struct intel_iommu *iommu);
struct iommu_domain *intel_nested_domain_alloc(struct iommu_domain *parent, struct iommu_domain *intel_nested_domain_alloc(struct iommu_domain *parent,
const struct iommu_user_data *user_data); const struct iommu_user_data *user_data);
struct device *device_rbtree_find(struct intel_iommu *iommu, u16 rid);
#ifdef CONFIG_INTEL_IOMMU_SVM #ifdef CONFIG_INTEL_IOMMU_SVM
void intel_svm_check(struct intel_iommu *iommu); void intel_svm_check(struct intel_iommu *iommu);
int intel_svm_enable_prq(struct intel_iommu *iommu); int intel_svm_enable_prq(struct intel_iommu *iommu);
int intel_svm_finish_prq(struct intel_iommu *iommu); int intel_svm_finish_prq(struct intel_iommu *iommu);
int intel_svm_page_response(struct device *dev, struct iommu_fault_event *evt, void intel_svm_page_response(struct device *dev, struct iopf_fault *evt,
struct iommu_page_response *msg); struct iommu_page_response *msg);
struct iommu_domain *intel_svm_domain_alloc(void); struct iommu_domain *intel_svm_domain_alloc(void);
void intel_svm_remove_dev_pasid(struct device *dev, ioasid_t pasid); void intel_svm_remove_dev_pasid(struct device *dev, ioasid_t pasid);
......
...@@ -214,6 +214,9 @@ devtlb_invalidation_with_pasid(struct intel_iommu *iommu, ...@@ -214,6 +214,9 @@ devtlb_invalidation_with_pasid(struct intel_iommu *iommu,
if (!info || !info->ats_enabled) if (!info || !info->ats_enabled)
return; return;
if (pci_dev_is_disconnected(to_pci_dev(dev)))
return;
sid = info->bus << 8 | info->devfn; sid = info->bus << 8 | info->devfn;
qdep = info->ats_qdep; qdep = info->ats_qdep;
pfsid = info->pfsid; pfsid = info->pfsid;
...@@ -667,3 +670,205 @@ int intel_pasid_setup_nested(struct intel_iommu *iommu, struct device *dev, ...@@ -667,3 +670,205 @@ int intel_pasid_setup_nested(struct intel_iommu *iommu, struct device *dev,
return 0; return 0;
} }
/*
* Interfaces to setup or teardown a pasid table to the scalable-mode
* context table entry:
*/
static void device_pasid_table_teardown(struct device *dev, u8 bus, u8 devfn)
{
struct device_domain_info *info = dev_iommu_priv_get(dev);
struct intel_iommu *iommu = info->iommu;
struct context_entry *context;
spin_lock(&iommu->lock);
context = iommu_context_addr(iommu, bus, devfn, false);
if (!context) {
spin_unlock(&iommu->lock);
return;
}
context_clear_entry(context);
__iommu_flush_cache(iommu, context, sizeof(*context));
spin_unlock(&iommu->lock);
/*
* Cache invalidation for changes to a scalable-mode context table
* entry.
*
* Section 6.5.3.3 of the VT-d spec:
* - Device-selective context-cache invalidation;
* - Domain-selective PASID-cache invalidation to affected domains
* (can be skipped if all PASID entries were not-present);
* - Domain-selective IOTLB invalidation to affected domains;
* - Global Device-TLB invalidation to affected functions.
*
* The iommu has been parked in the blocking state. All domains have
* been detached from the device or PASID. The PASID and IOTLB caches
* have been invalidated during the domain detach path.
*/
iommu->flush.flush_context(iommu, 0, PCI_DEVID(bus, devfn),
DMA_CCMD_MASK_NOBIT, DMA_CCMD_DEVICE_INVL);
devtlb_invalidation_with_pasid(iommu, dev, IOMMU_NO_PASID);
}
static int pci_pasid_table_teardown(struct pci_dev *pdev, u16 alias, void *data)
{
struct device *dev = data;
if (dev == &pdev->dev)
device_pasid_table_teardown(dev, PCI_BUS_NUM(alias), alias & 0xff);
return 0;
}
void intel_pasid_teardown_sm_context(struct device *dev)
{
struct device_domain_info *info = dev_iommu_priv_get(dev);
if (!dev_is_pci(dev)) {
device_pasid_table_teardown(dev, info->bus, info->devfn);
return;
}
pci_for_each_dma_alias(to_pci_dev(dev), pci_pasid_table_teardown, dev);
}
/*
* Get the PASID directory size for scalable mode context entry.
* Value of X in the PDTS field of a scalable mode context entry
* indicates PASID directory with 2^(X + 7) entries.
*/
static unsigned long context_get_sm_pds(struct pasid_table *table)
{
unsigned long pds, max_pde;
max_pde = table->max_pasid >> PASID_PDE_SHIFT;
pds = find_first_bit(&max_pde, MAX_NR_PASID_BITS);
if (pds < 7)
return 0;
return pds - 7;
}
static int context_entry_set_pasid_table(struct context_entry *context,
struct device *dev)
{
struct device_domain_info *info = dev_iommu_priv_get(dev);
struct pasid_table *table = info->pasid_table;
struct intel_iommu *iommu = info->iommu;
unsigned long pds;
context_clear_entry(context);
pds = context_get_sm_pds(table);
context->lo = (u64)virt_to_phys(table->table) | context_pdts(pds);
context_set_sm_rid2pasid(context, IOMMU_NO_PASID);
if (info->ats_supported)
context_set_sm_dte(context);
if (info->pri_supported)
context_set_sm_pre(context);
if (info->pasid_supported)
context_set_pasid(context);
context_set_fault_enable(context);
context_set_present(context);
__iommu_flush_cache(iommu, context, sizeof(*context));
return 0;
}
static int device_pasid_table_setup(struct device *dev, u8 bus, u8 devfn)
{
struct device_domain_info *info = dev_iommu_priv_get(dev);
struct intel_iommu *iommu = info->iommu;
struct context_entry *context;
spin_lock(&iommu->lock);
context = iommu_context_addr(iommu, bus, devfn, true);
if (!context) {
spin_unlock(&iommu->lock);
return -ENOMEM;
}
if (context_present(context) && !context_copied(iommu, bus, devfn)) {
spin_unlock(&iommu->lock);
return 0;
}
if (context_copied(iommu, bus, devfn)) {
context_clear_entry(context);
__iommu_flush_cache(iommu, context, sizeof(*context));
/*
* For kdump cases, old valid entries may be cached due to
* the in-flight DMA and copied pgtable, but there is no
* unmapping behaviour for them, thus we need explicit cache
* flushes for all affected domain IDs and PASIDs used in
* the copied PASID table. Given that we have no idea about
* which domain IDs and PASIDs were used in the copied tables,
* upgrade them to global PASID and IOTLB cache invalidation.
*/
iommu->flush.flush_context(iommu, 0,
PCI_DEVID(bus, devfn),
DMA_CCMD_MASK_NOBIT,
DMA_CCMD_DEVICE_INVL);
qi_flush_pasid_cache(iommu, 0, QI_PC_GLOBAL, 0);
iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_GLOBAL_FLUSH);
devtlb_invalidation_with_pasid(iommu, dev, IOMMU_NO_PASID);
/*
* At this point, the device is supposed to finish reset at
* its driver probe stage, so no in-flight DMA will exist,
* and we don't need to worry anymore hereafter.
*/
clear_context_copied(iommu, bus, devfn);
}
context_entry_set_pasid_table(context, dev);
spin_unlock(&iommu->lock);
/*
* It's a non-present to present mapping. If hardware doesn't cache
* non-present entry we don't need to flush the caches. If it does
* cache non-present entries, then it does so in the special
* domain #0, which we have to flush:
*/
if (cap_caching_mode(iommu->cap)) {
iommu->flush.flush_context(iommu, 0,
PCI_DEVID(bus, devfn),
DMA_CCMD_MASK_NOBIT,
DMA_CCMD_DEVICE_INVL);
iommu->flush.flush_iotlb(iommu, 0, 0, 0, DMA_TLB_DSI_FLUSH);
}
return 0;
}
static int pci_pasid_table_setup(struct pci_dev *pdev, u16 alias, void *data)
{
struct device *dev = data;
if (dev != &pdev->dev)
return 0;
return device_pasid_table_setup(dev, PCI_BUS_NUM(alias), alias & 0xff);
}
/*
* Set the device's PASID table to its context table entry.
*
* The PASID table is set to the context entries of both device itself
* and its alias requester ID for DMA.
*/
int intel_pasid_setup_sm_context(struct device *dev)
{
struct device_domain_info *info = dev_iommu_priv_get(dev);
if (!dev_is_pci(dev))
return device_pasid_table_setup(dev, info->bus, info->devfn);
return pci_for_each_dma_alias(to_pci_dev(dev), pci_pasid_table_setup, dev);
}
...@@ -318,4 +318,6 @@ void intel_pasid_tear_down_entry(struct intel_iommu *iommu, ...@@ -318,4 +318,6 @@ void intel_pasid_tear_down_entry(struct intel_iommu *iommu,
bool fault_ignore); bool fault_ignore);
void intel_pasid_setup_page_snoop_control(struct intel_iommu *iommu, void intel_pasid_setup_page_snoop_control(struct intel_iommu *iommu,
struct device *dev, u32 pasid); struct device *dev, u32 pasid);
int intel_pasid_setup_sm_context(struct device *dev);
void intel_pasid_teardown_sm_context(struct device *dev);
#endif /* __INTEL_PASID_H */ #endif /* __INTEL_PASID_H */
...@@ -33,7 +33,7 @@ int dmar_latency_enable(struct intel_iommu *iommu, enum latency_type type) ...@@ -33,7 +33,7 @@ int dmar_latency_enable(struct intel_iommu *iommu, enum latency_type type)
spin_lock_irqsave(&latency_lock, flags); spin_lock_irqsave(&latency_lock, flags);
if (!iommu->perf_statistic) { if (!iommu->perf_statistic) {
iommu->perf_statistic = kzalloc(sizeof(*lstat) * DMAR_LATENCY_NUM, iommu->perf_statistic = kcalloc(DMAR_LATENCY_NUM, sizeof(*lstat),
GFP_ATOMIC); GFP_ATOMIC);
if (!iommu->perf_statistic) { if (!iommu->perf_statistic) {
ret = -ENOMEM; ret = -ENOMEM;
......
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
#include "iommu.h" #include "iommu.h"
#include "pasid.h" #include "pasid.h"
#include "perf.h" #include "perf.h"
#include "../iommu-sva.h"
#include "trace.h" #include "trace.h"
static irqreturn_t prq_event_thread(int irq, void *d); static irqreturn_t prq_event_thread(int irq, void *d);
...@@ -315,10 +314,11 @@ static int pasid_to_svm_sdev(struct device *dev, unsigned int pasid, ...@@ -315,10 +314,11 @@ static int pasid_to_svm_sdev(struct device *dev, unsigned int pasid,
return 0; return 0;
} }
static int intel_svm_bind_mm(struct intel_iommu *iommu, struct device *dev, static int intel_svm_set_dev_pasid(struct iommu_domain *domain,
struct iommu_domain *domain, ioasid_t pasid) struct device *dev, ioasid_t pasid)
{ {
struct device_domain_info *info = dev_iommu_priv_get(dev); struct device_domain_info *info = dev_iommu_priv_get(dev);
struct intel_iommu *iommu = info->iommu;
struct mm_struct *mm = domain->mm; struct mm_struct *mm = domain->mm;
struct intel_svm_dev *sdev; struct intel_svm_dev *sdev;
struct intel_svm *svm; struct intel_svm *svm;
...@@ -360,7 +360,6 @@ static int intel_svm_bind_mm(struct intel_iommu *iommu, struct device *dev, ...@@ -360,7 +360,6 @@ static int intel_svm_bind_mm(struct intel_iommu *iommu, struct device *dev,
sdev->iommu = iommu; sdev->iommu = iommu;
sdev->did = FLPT_DEFAULT_DID; sdev->did = FLPT_DEFAULT_DID;
sdev->sid = PCI_DEVID(info->bus, info->devfn); sdev->sid = PCI_DEVID(info->bus, info->devfn);
init_rcu_head(&sdev->rcu);
if (info->ats_enabled) { if (info->ats_enabled) {
sdev->qdep = info->ats_qdep; sdev->qdep = info->ats_qdep;
if (sdev->qdep >= QI_DEV_EIOTLB_MAX_INVS) if (sdev->qdep >= QI_DEV_EIOTLB_MAX_INVS)
...@@ -408,13 +407,6 @@ void intel_svm_remove_dev_pasid(struct device *dev, u32 pasid) ...@@ -408,13 +407,6 @@ void intel_svm_remove_dev_pasid(struct device *dev, u32 pasid)
if (svm->notifier.ops) if (svm->notifier.ops)
mmu_notifier_unregister(&svm->notifier, mm); mmu_notifier_unregister(&svm->notifier, mm);
pasid_private_remove(svm->pasid); pasid_private_remove(svm->pasid);
/*
* We mandate that no page faults may be outstanding
* for the PASID when intel_svm_unbind_mm() is called.
* If that is not obeyed, subtle errors will happen.
* Let's make them less subtle...
*/
memset(svm, 0x6b, sizeof(*svm));
kfree(svm); kfree(svm);
} }
} }
...@@ -562,16 +554,12 @@ static int prq_to_iommu_prot(struct page_req_dsc *req) ...@@ -562,16 +554,12 @@ static int prq_to_iommu_prot(struct page_req_dsc *req)
return prot; return prot;
} }
static int intel_svm_prq_report(struct intel_iommu *iommu, struct device *dev, static void intel_svm_prq_report(struct intel_iommu *iommu, struct device *dev,
struct page_req_dsc *desc) struct page_req_dsc *desc)
{ {
struct iommu_fault_event event; struct iopf_fault event = { };
if (!dev || !dev_is_pci(dev))
return -ENODEV;
/* Fill in event data for device specific processing */ /* Fill in event data for device specific processing */
memset(&event, 0, sizeof(struct iommu_fault_event));
event.fault.type = IOMMU_FAULT_PAGE_REQ; event.fault.type = IOMMU_FAULT_PAGE_REQ;
event.fault.prm.addr = (u64)desc->addr << VTD_PAGE_SHIFT; event.fault.prm.addr = (u64)desc->addr << VTD_PAGE_SHIFT;
event.fault.prm.pasid = desc->pasid; event.fault.prm.pasid = desc->pasid;
...@@ -603,7 +591,7 @@ static int intel_svm_prq_report(struct intel_iommu *iommu, struct device *dev, ...@@ -603,7 +591,7 @@ static int intel_svm_prq_report(struct intel_iommu *iommu, struct device *dev,
event.fault.prm.private_data[0] = ktime_to_ns(ktime_get()); event.fault.prm.private_data[0] = ktime_to_ns(ktime_get());
} }
return iommu_report_device_fault(dev, &event); iommu_report_device_fault(dev, &event);
} }
static void handle_bad_prq_event(struct intel_iommu *iommu, static void handle_bad_prq_event(struct intel_iommu *iommu,
...@@ -650,7 +638,7 @@ static irqreturn_t prq_event_thread(int irq, void *d) ...@@ -650,7 +638,7 @@ static irqreturn_t prq_event_thread(int irq, void *d)
struct intel_iommu *iommu = d; struct intel_iommu *iommu = d;
struct page_req_dsc *req; struct page_req_dsc *req;
int head, tail, handled; int head, tail, handled;
struct pci_dev *pdev; struct device *dev;
u64 address; u64 address;
/* /*
...@@ -696,23 +684,22 @@ static irqreturn_t prq_event_thread(int irq, void *d) ...@@ -696,23 +684,22 @@ static irqreturn_t prq_event_thread(int irq, void *d)
if (unlikely(req->lpig && !req->rd_req && !req->wr_req)) if (unlikely(req->lpig && !req->rd_req && !req->wr_req))
goto prq_advance; goto prq_advance;
pdev = pci_get_domain_bus_and_slot(iommu->segment,
PCI_BUS_NUM(req->rid),
req->rid & 0xff);
/* /*
* If prq is to be handled outside iommu driver via receiver of * If prq is to be handled outside iommu driver via receiver of
* the fault notifiers, we skip the page response here. * the fault notifiers, we skip the page response here.
*/ */
if (!pdev) mutex_lock(&iommu->iopf_lock);
dev = device_rbtree_find(iommu, req->rid);
if (!dev) {
mutex_unlock(&iommu->iopf_lock);
goto bad_req; goto bad_req;
}
if (intel_svm_prq_report(iommu, &pdev->dev, req)) intel_svm_prq_report(iommu, dev, req);
handle_bad_prq_event(iommu, req, QI_RESP_INVALID); trace_prq_report(iommu, dev, req->qw_0, req->qw_1,
else
trace_prq_report(iommu, &pdev->dev, req->qw_0, req->qw_1,
req->priv_data[0], req->priv_data[1], req->priv_data[0], req->priv_data[1],
iommu->prq_seq_number++); iommu->prq_seq_number++);
pci_dev_put(pdev); mutex_unlock(&iommu->iopf_lock);
prq_advance: prq_advance:
head = (head + sizeof(*req)) & PRQ_RING_MASK; head = (head + sizeof(*req)) & PRQ_RING_MASK;
} }
...@@ -742,8 +729,7 @@ static irqreturn_t prq_event_thread(int irq, void *d) ...@@ -742,8 +729,7 @@ static irqreturn_t prq_event_thread(int irq, void *d)
return IRQ_RETVAL(handled); return IRQ_RETVAL(handled);
} }
int intel_svm_page_response(struct device *dev, void intel_svm_page_response(struct device *dev, struct iopf_fault *evt,
struct iommu_fault_event *evt,
struct iommu_page_response *msg) struct iommu_page_response *msg)
{ {
struct device_domain_info *info = dev_iommu_priv_get(dev); struct device_domain_info *info = dev_iommu_priv_get(dev);
...@@ -753,7 +739,6 @@ int intel_svm_page_response(struct device *dev, ...@@ -753,7 +739,6 @@ int intel_svm_page_response(struct device *dev,
bool private_present; bool private_present;
bool pasid_present; bool pasid_present;
bool last_page; bool last_page;
int ret = 0;
u16 sid; u16 sid;
prm = &evt->fault.prm; prm = &evt->fault.prm;
...@@ -762,16 +747,6 @@ int intel_svm_page_response(struct device *dev, ...@@ -762,16 +747,6 @@ int intel_svm_page_response(struct device *dev,
private_present = prm->flags & IOMMU_FAULT_PAGE_REQUEST_PRIV_DATA; private_present = prm->flags & IOMMU_FAULT_PAGE_REQUEST_PRIV_DATA;
last_page = prm->flags & IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE; last_page = prm->flags & IOMMU_FAULT_PAGE_REQUEST_LAST_PAGE;
if (!pasid_present) {
ret = -EINVAL;
goto out;
}
if (prm->pasid == 0 || prm->pasid >= PASID_MAX) {
ret = -EINVAL;
goto out;
}
/* /*
* Per VT-d spec. v3.0 ch7.7, system software must respond * Per VT-d spec. v3.0 ch7.7, system software must respond
* with page group response if private data is present (PDP) * with page group response if private data is present (PDP)
...@@ -800,17 +775,6 @@ int intel_svm_page_response(struct device *dev, ...@@ -800,17 +775,6 @@ int intel_svm_page_response(struct device *dev,
qi_submit_sync(iommu, &desc, 1, 0); qi_submit_sync(iommu, &desc, 1, 0);
} }
out:
return ret;
}
static int intel_svm_set_dev_pasid(struct iommu_domain *domain,
struct device *dev, ioasid_t pasid)
{
struct device_domain_info *info = dev_iommu_priv_get(dev);
struct intel_iommu *iommu = info->iommu;
return intel_svm_bind_mm(iommu, dev, domain, pasid);
} }
static void intel_svm_domain_free(struct iommu_domain *domain) static void intel_svm_domain_free(struct iommu_domain *domain)
......
This diff is collapsed.
...@@ -21,10 +21,11 @@ int iommu_group_replace_domain(struct iommu_group *group, ...@@ -21,10 +21,11 @@ int iommu_group_replace_domain(struct iommu_group *group,
struct iommu_domain *new_domain); struct iommu_domain *new_domain);
int iommu_device_register_bus(struct iommu_device *iommu, int iommu_device_register_bus(struct iommu_device *iommu,
const struct iommu_ops *ops, struct bus_type *bus, const struct iommu_ops *ops,
const struct bus_type *bus,
struct notifier_block *nb); struct notifier_block *nb);
void iommu_device_unregister_bus(struct iommu_device *iommu, void iommu_device_unregister_bus(struct iommu_device *iommu,
struct bus_type *bus, const struct bus_type *bus,
struct notifier_block *nb); struct notifier_block *nb);
#endif /* __LINUX_IOMMU_PRIV_H */ #endif /* __LINUX_IOMMU_PRIV_H */
...@@ -7,7 +7,7 @@ ...@@ -7,7 +7,7 @@
#include <linux/sched/mm.h> #include <linux/sched/mm.h>
#include <linux/iommu.h> #include <linux/iommu.h>
#include "iommu-sva.h" #include "iommu-priv.h"
static DEFINE_MUTEX(iommu_sva_lock); static DEFINE_MUTEX(iommu_sva_lock);
...@@ -176,15 +176,25 @@ u32 iommu_sva_get_pasid(struct iommu_sva *handle) ...@@ -176,15 +176,25 @@ u32 iommu_sva_get_pasid(struct iommu_sva *handle)
} }
EXPORT_SYMBOL_GPL(iommu_sva_get_pasid); EXPORT_SYMBOL_GPL(iommu_sva_get_pasid);
void mm_pasid_drop(struct mm_struct *mm)
{
struct iommu_mm_data *iommu_mm = mm->iommu_mm;
if (!iommu_mm)
return;
iommu_free_global_pasid(iommu_mm->pasid);
kfree(iommu_mm);
}
/* /*
* I/O page fault handler for SVA * I/O page fault handler for SVA
*/ */
enum iommu_page_response_code static enum iommu_page_response_code
iommu_sva_handle_iopf(struct iommu_fault *fault, void *data) iommu_sva_handle_mm(struct iommu_fault *fault, struct mm_struct *mm)
{ {
vm_fault_t ret; vm_fault_t ret;
struct vm_area_struct *vma; struct vm_area_struct *vma;
struct mm_struct *mm = data;
unsigned int access_flags = 0; unsigned int access_flags = 0;
unsigned int fault_flags = FAULT_FLAG_REMOTE; unsigned int fault_flags = FAULT_FLAG_REMOTE;
struct iommu_fault_page_request *prm = &fault->prm; struct iommu_fault_page_request *prm = &fault->prm;
...@@ -234,13 +244,54 @@ iommu_sva_handle_iopf(struct iommu_fault *fault, void *data) ...@@ -234,13 +244,54 @@ iommu_sva_handle_iopf(struct iommu_fault *fault, void *data)
return status; return status;
} }
void mm_pasid_drop(struct mm_struct *mm) static void iommu_sva_handle_iopf(struct work_struct *work)
{ {
struct iommu_mm_data *iommu_mm = mm->iommu_mm; struct iopf_fault *iopf;
struct iopf_group *group;
enum iommu_page_response_code status = IOMMU_PAGE_RESP_SUCCESS;
if (!iommu_mm) group = container_of(work, struct iopf_group, work);
return; list_for_each_entry(iopf, &group->faults, list) {
/*
* For the moment, errors are sticky: don't handle subsequent
* faults in the group if there is an error.
*/
if (status != IOMMU_PAGE_RESP_SUCCESS)
break;
iommu_free_global_pasid(iommu_mm->pasid); status = iommu_sva_handle_mm(&iopf->fault, group->domain->mm);
kfree(iommu_mm); }
iopf_group_response(group, status);
iopf_free_group(group);
}
static int iommu_sva_iopf_handler(struct iopf_group *group)
{
struct iommu_fault_param *fault_param = group->fault_param;
INIT_WORK(&group->work, iommu_sva_handle_iopf);
if (!queue_work(fault_param->queue->wq, &group->work))
return -EBUSY;
return 0;
}
struct iommu_domain *iommu_sva_domain_alloc(struct device *dev,
struct mm_struct *mm)
{
const struct iommu_ops *ops = dev_iommu_ops(dev);
struct iommu_domain *domain;
domain = ops->domain_alloc(IOMMU_DOMAIN_SVA);
if (!domain)
return NULL;
domain->type = IOMMU_DOMAIN_SVA;
mmgrab(mm);
domain->mm = mm;
domain->owner = ops;
domain->iopf_handler = iommu_sva_iopf_handler;
return domain;
} }
/* SPDX-License-Identifier: GPL-2.0 */
/*
* SVA library for IOMMU drivers
*/
#ifndef _IOMMU_SVA_H
#define _IOMMU_SVA_H
#include <linux/mm_types.h>
/* I/O Page fault */
struct device;
struct iommu_fault;
struct iopf_queue;
#ifdef CONFIG_IOMMU_SVA
int iommu_queue_iopf(struct iommu_fault *fault, void *cookie);
int iopf_queue_add_device(struct iopf_queue *queue, struct device *dev);
int iopf_queue_remove_device(struct iopf_queue *queue,
struct device *dev);
int iopf_queue_flush_dev(struct device *dev);
struct iopf_queue *iopf_queue_alloc(const char *name);
void iopf_queue_free(struct iopf_queue *queue);
int iopf_queue_discard_partial(struct iopf_queue *queue);
enum iommu_page_response_code
iommu_sva_handle_iopf(struct iommu_fault *fault, void *data);
#else /* CONFIG_IOMMU_SVA */
static inline int iommu_queue_iopf(struct iommu_fault *fault, void *cookie)
{
return -ENODEV;
}
static inline int iopf_queue_add_device(struct iopf_queue *queue,
struct device *dev)
{
return -ENODEV;
}
static inline int iopf_queue_remove_device(struct iopf_queue *queue,
struct device *dev)
{
return -ENODEV;
}
static inline int iopf_queue_flush_dev(struct device *dev)
{
return -ENODEV;
}
static inline struct iopf_queue *iopf_queue_alloc(const char *name)
{
return NULL;
}
static inline void iopf_queue_free(struct iopf_queue *queue)
{
}
static inline int iopf_queue_discard_partial(struct iopf_queue *queue)
{
return -ENODEV;
}
static inline enum iommu_page_response_code
iommu_sva_handle_iopf(struct iommu_fault *fault, void *data)
{
return IOMMU_PAGE_RESP_INVALID;
}
#endif /* CONFIG_IOMMU_SVA */
#endif /* _IOMMU_SVA_H */
This diff is collapsed.
...@@ -24,24 +24,8 @@ static bool iova_rcache_insert(struct iova_domain *iovad, ...@@ -24,24 +24,8 @@ static bool iova_rcache_insert(struct iova_domain *iovad,
static unsigned long iova_rcache_get(struct iova_domain *iovad, static unsigned long iova_rcache_get(struct iova_domain *iovad,
unsigned long size, unsigned long size,
unsigned long limit_pfn); unsigned long limit_pfn);
static void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad);
static void free_iova_rcaches(struct iova_domain *iovad); static void free_iova_rcaches(struct iova_domain *iovad);
static void free_cpu_cached_iovas(unsigned int cpu, struct iova_domain *iovad);
unsigned long iova_rcache_range(void)
{
return PAGE_SIZE << (IOVA_RANGE_CACHE_MAX_SIZE - 1);
}
static int iova_cpuhp_dead(unsigned int cpu, struct hlist_node *node)
{
struct iova_domain *iovad;
iovad = hlist_entry_safe(node, struct iova_domain, cpuhp_dead);
free_cpu_cached_iovas(cpu, iovad);
return 0;
}
static void free_global_cached_iovas(struct iova_domain *iovad); static void free_global_cached_iovas(struct iova_domain *iovad);
static struct iova *to_iova(struct rb_node *node) static struct iova *to_iova(struct rb_node *node)
...@@ -252,54 +236,6 @@ static void free_iova_mem(struct iova *iova) ...@@ -252,54 +236,6 @@ static void free_iova_mem(struct iova *iova)
kmem_cache_free(iova_cache, iova); kmem_cache_free(iova_cache, iova);
} }
int iova_cache_get(void)
{
mutex_lock(&iova_cache_mutex);
if (!iova_cache_users) {
int ret;
ret = cpuhp_setup_state_multi(CPUHP_IOMMU_IOVA_DEAD, "iommu/iova:dead", NULL,
iova_cpuhp_dead);
if (ret) {
mutex_unlock(&iova_cache_mutex);
pr_err("Couldn't register cpuhp handler\n");
return ret;
}
iova_cache = kmem_cache_create(
"iommu_iova", sizeof(struct iova), 0,
SLAB_HWCACHE_ALIGN, NULL);
if (!iova_cache) {
cpuhp_remove_multi_state(CPUHP_IOMMU_IOVA_DEAD);
mutex_unlock(&iova_cache_mutex);
pr_err("Couldn't create iova cache\n");
return -ENOMEM;
}
}
iova_cache_users++;
mutex_unlock(&iova_cache_mutex);
return 0;
}
EXPORT_SYMBOL_GPL(iova_cache_get);
void iova_cache_put(void)
{
mutex_lock(&iova_cache_mutex);
if (WARN_ON(!iova_cache_users)) {
mutex_unlock(&iova_cache_mutex);
return;
}
iova_cache_users--;
if (!iova_cache_users) {
cpuhp_remove_multi_state(CPUHP_IOMMU_IOVA_DEAD);
kmem_cache_destroy(iova_cache);
}
mutex_unlock(&iova_cache_mutex);
}
EXPORT_SYMBOL_GPL(iova_cache_put);
/** /**
* alloc_iova - allocates an iova * alloc_iova - allocates an iova
* @iovad: - iova domain in question * @iovad: - iova domain in question
...@@ -654,11 +590,18 @@ struct iova_rcache { ...@@ -654,11 +590,18 @@ struct iova_rcache {
struct delayed_work work; struct delayed_work work;
}; };
static struct kmem_cache *iova_magazine_cache;
unsigned long iova_rcache_range(void)
{
return PAGE_SIZE << (IOVA_RANGE_CACHE_MAX_SIZE - 1);
}
static struct iova_magazine *iova_magazine_alloc(gfp_t flags) static struct iova_magazine *iova_magazine_alloc(gfp_t flags)
{ {
struct iova_magazine *mag; struct iova_magazine *mag;
mag = kmalloc(sizeof(*mag), flags); mag = kmem_cache_alloc(iova_magazine_cache, flags);
if (mag) if (mag)
mag->size = 0; mag->size = 0;
...@@ -667,7 +610,7 @@ static struct iova_magazine *iova_magazine_alloc(gfp_t flags) ...@@ -667,7 +610,7 @@ static struct iova_magazine *iova_magazine_alloc(gfp_t flags)
static void iova_magazine_free(struct iova_magazine *mag) static void iova_magazine_free(struct iova_magazine *mag)
{ {
kfree(mag); kmem_cache_free(iova_magazine_cache, mag);
} }
static void static void
...@@ -990,5 +933,71 @@ static void free_global_cached_iovas(struct iova_domain *iovad) ...@@ -990,5 +933,71 @@ static void free_global_cached_iovas(struct iova_domain *iovad)
spin_unlock_irqrestore(&rcache->lock, flags); spin_unlock_irqrestore(&rcache->lock, flags);
} }
} }
static int iova_cpuhp_dead(unsigned int cpu, struct hlist_node *node)
{
struct iova_domain *iovad;
iovad = hlist_entry_safe(node, struct iova_domain, cpuhp_dead);
free_cpu_cached_iovas(cpu, iovad);
return 0;
}
int iova_cache_get(void)
{
int err = -ENOMEM;
mutex_lock(&iova_cache_mutex);
if (!iova_cache_users) {
iova_cache = kmem_cache_create("iommu_iova", sizeof(struct iova), 0,
SLAB_HWCACHE_ALIGN, NULL);
if (!iova_cache)
goto out_err;
iova_magazine_cache = kmem_cache_create("iommu_iova_magazine",
sizeof(struct iova_magazine),
0, SLAB_HWCACHE_ALIGN, NULL);
if (!iova_magazine_cache)
goto out_err;
err = cpuhp_setup_state_multi(CPUHP_IOMMU_IOVA_DEAD, "iommu/iova:dead",
NULL, iova_cpuhp_dead);
if (err) {
pr_err("IOVA: Couldn't register cpuhp handler: %pe\n", ERR_PTR(err));
goto out_err;
}
}
iova_cache_users++;
mutex_unlock(&iova_cache_mutex);
return 0;
out_err:
kmem_cache_destroy(iova_cache);
kmem_cache_destroy(iova_magazine_cache);
mutex_unlock(&iova_cache_mutex);
return err;
}
EXPORT_SYMBOL_GPL(iova_cache_get);
void iova_cache_put(void)
{
mutex_lock(&iova_cache_mutex);
if (WARN_ON(!iova_cache_users)) {
mutex_unlock(&iova_cache_mutex);
return;
}
iova_cache_users--;
if (!iova_cache_users) {
cpuhp_remove_multi_state(CPUHP_IOMMU_IOVA_DEAD);
kmem_cache_destroy(iova_cache);
kmem_cache_destroy(iova_magazine_cache);
}
mutex_unlock(&iova_cache_mutex);
}
EXPORT_SYMBOL_GPL(iova_cache_put);
MODULE_AUTHOR("Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>"); MODULE_AUTHOR("Anil S Keshavamurthy <anil.s.keshavamurthy@intel.com>");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL");
...@@ -709,7 +709,7 @@ static phys_addr_t ipmmu_iova_to_phys(struct iommu_domain *io_domain, ...@@ -709,7 +709,7 @@ static phys_addr_t ipmmu_iova_to_phys(struct iommu_domain *io_domain,
} }
static int ipmmu_init_platform_device(struct device *dev, static int ipmmu_init_platform_device(struct device *dev,
struct of_phandle_args *args) const struct of_phandle_args *args)
{ {
struct platform_device *ipmmu_pdev; struct platform_device *ipmmu_pdev;
...@@ -773,7 +773,7 @@ static bool ipmmu_device_is_allowed(struct device *dev) ...@@ -773,7 +773,7 @@ static bool ipmmu_device_is_allowed(struct device *dev)
} }
static int ipmmu_of_xlate(struct device *dev, static int ipmmu_of_xlate(struct device *dev,
struct of_phandle_args *spec) const struct of_phandle_args *spec)
{ {
if (!ipmmu_device_is_allowed(dev)) if (!ipmmu_device_is_allowed(dev))
return -ENODEV; return -ENODEV;
...@@ -1005,7 +1005,6 @@ static const struct of_device_id ipmmu_of_ids[] = { ...@@ -1005,7 +1005,6 @@ static const struct of_device_id ipmmu_of_ids[] = {
static int ipmmu_probe(struct platform_device *pdev) static int ipmmu_probe(struct platform_device *pdev)
{ {
struct ipmmu_vmsa_device *mmu; struct ipmmu_vmsa_device *mmu;
struct resource *res;
int irq; int irq;
int ret; int ret;
...@@ -1025,8 +1024,7 @@ static int ipmmu_probe(struct platform_device *pdev) ...@@ -1025,8 +1024,7 @@ static int ipmmu_probe(struct platform_device *pdev)
return ret; return ret;
/* Map I/O memory and request IRQ. */ /* Map I/O memory and request IRQ. */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0); mmu->base = devm_platform_ioremap_resource(pdev, 0);
mmu->base = devm_ioremap_resource(&pdev->dev, res);
if (IS_ERR(mmu->base)) if (IS_ERR(mmu->base))
return PTR_ERR(mmu->base); return PTR_ERR(mmu->base);
...@@ -1123,7 +1121,6 @@ static void ipmmu_remove(struct platform_device *pdev) ...@@ -1123,7 +1121,6 @@ static void ipmmu_remove(struct platform_device *pdev)
ipmmu_device_reset(mmu); ipmmu_device_reset(mmu);
} }
#ifdef CONFIG_PM_SLEEP
static int ipmmu_resume_noirq(struct device *dev) static int ipmmu_resume_noirq(struct device *dev)
{ {
struct ipmmu_vmsa_device *mmu = dev_get_drvdata(dev); struct ipmmu_vmsa_device *mmu = dev_get_drvdata(dev);
...@@ -1153,18 +1150,14 @@ static int ipmmu_resume_noirq(struct device *dev) ...@@ -1153,18 +1150,14 @@ static int ipmmu_resume_noirq(struct device *dev)
} }
static const struct dev_pm_ops ipmmu_pm = { static const struct dev_pm_ops ipmmu_pm = {
SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, ipmmu_resume_noirq) NOIRQ_SYSTEM_SLEEP_PM_OPS(NULL, ipmmu_resume_noirq)
}; };
#define DEV_PM_OPS &ipmmu_pm
#else
#define DEV_PM_OPS NULL
#endif /* CONFIG_PM_SLEEP */
static struct platform_driver ipmmu_driver = { static struct platform_driver ipmmu_driver = {
.driver = { .driver = {
.name = "ipmmu-vmsa", .name = "ipmmu-vmsa",
.of_match_table = of_match_ptr(ipmmu_of_ids), .of_match_table = ipmmu_of_ids,
.pm = DEV_PM_OPS, .pm = pm_sleep_ptr(&ipmmu_pm),
}, },
.probe = ipmmu_probe, .probe = ipmmu_probe,
.remove_new = ipmmu_remove, .remove_new = ipmmu_remove,
......
...@@ -99,7 +99,8 @@ int __init irq_remapping_prepare(void) ...@@ -99,7 +99,8 @@ int __init irq_remapping_prepare(void)
if (disable_irq_remap) if (disable_irq_remap)
return -ENOSYS; return -ENOSYS;
if (intel_irq_remap_ops.prepare() == 0) if (IS_ENABLED(CONFIG_INTEL_IOMMU) &&
intel_irq_remap_ops.prepare() == 0)
remap_ops = &intel_irq_remap_ops; remap_ops = &intel_irq_remap_ops;
else if (IS_ENABLED(CONFIG_AMD_IOMMU) && else if (IS_ENABLED(CONFIG_AMD_IOMMU) &&
amd_iommu_irq_ops.prepare() == 0) amd_iommu_irq_ops.prepare() == 0)
......
...@@ -598,7 +598,7 @@ static void print_ctx_regs(void __iomem *base, int ctx) ...@@ -598,7 +598,7 @@ static void print_ctx_regs(void __iomem *base, int ctx)
static int insert_iommu_master(struct device *dev, static int insert_iommu_master(struct device *dev,
struct msm_iommu_dev **iommu, struct msm_iommu_dev **iommu,
struct of_phandle_args *spec) const struct of_phandle_args *spec)
{ {
struct msm_iommu_ctx_dev *master = dev_iommu_priv_get(dev); struct msm_iommu_ctx_dev *master = dev_iommu_priv_get(dev);
int sid; int sid;
...@@ -626,7 +626,7 @@ static int insert_iommu_master(struct device *dev, ...@@ -626,7 +626,7 @@ static int insert_iommu_master(struct device *dev,
} }
static int qcom_iommu_of_xlate(struct device *dev, static int qcom_iommu_of_xlate(struct device *dev,
struct of_phandle_args *spec) const struct of_phandle_args *spec)
{ {
struct msm_iommu_dev *iommu = NULL, *iter; struct msm_iommu_dev *iommu = NULL, *iter;
unsigned long flags; unsigned long flags;
......
This diff is collapsed.
This diff is collapsed.
...@@ -29,7 +29,7 @@ static int of_iommu_xlate(struct device *dev, ...@@ -29,7 +29,7 @@ static int of_iommu_xlate(struct device *dev,
!of_device_is_available(iommu_spec->np)) !of_device_is_available(iommu_spec->np))
return -ENODEV; return -ENODEV;
ret = iommu_fwspec_init(dev, &iommu_spec->np->fwnode, ops); ret = iommu_fwspec_init(dev, fwnode, ops);
if (ret) if (ret)
return ret; return ret;
/* /*
......
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment