[PATCH v2 1/1] iommu/vt-d: Fix list_add double add when enabling VMD and scalable mode

2022-02-16 Thread Adrian Huang
From: Adrian Huang 

When enabling VMD and IOMMU scalable mode, the following kernel panic
call trace/kernel log is shown in Eagle Stream platform (Sapphire Rapids
CPU) during booting:

pci :59:00.5: Adding to iommu group 42
...
vmd :59:00.5: PCI host bridge to bus 1:80
pci 1:80:01.0: [8086:352a] type 01 class 0x060400
pci 1:80:01.0: reg 0x10: [mem 0x-0x0001 64bit]
pci 1:80:01.0: enabling Extended Tags
pci 1:80:01.0: PME# supported from D0 D3hot D3cold
pci 1:80:01.0: DMAR: Setup RID2PASID failed
pci 1:80:01.0: Failed to add to iommu group 42: -16
pci 1:80:03.0: [8086:352b] type 01 class 0x060400
pci 1:80:03.0: reg 0x10: [mem 0x-0x0001 64bit]
pci 1:80:03.0: enabling Extended Tags
pci 1:80:03.0: PME# supported from D0 D3hot D3cold
list_add double add: new=ff4d61160b74b8a0, prev=ff4d611d8e245c10, 
next=ff4d61160b74b8a0.
[ cut here ]
kernel BUG at lib/list_debug.c:29!
invalid opcode:  [#1] PREEMPT SMP NOPTI
CPU: 0 PID: 7 Comm: kworker/0:1 Not tainted 5.17.0-rc3+ #7
Hardware name: Lenovo ThinkSystem SR650V3/SB27A86647, BIOS ESE101Y-1.00 
01/13/2022
Workqueue: events work_for_cpu_fn
RIP: 0010:__list_add_valid.cold+0x26/0x3f
Code: 9a 4a ab ff 4c 89 c1 48 c7 c7 40 0c d9 9e e8 b9 b1 fe ff 0f 0b 48 89 f2 
4c 89 c1 48 89 fe 48 c7 c7 f0 0c d9 9e e8 a2 b1 fe ff <0f> 0b 48 89 d1 4c 89 c6 
4c 89 ca 48 c7 c7 98 0c d9 9e e8 8b b1 fe
RSP: :ff5ad434865b3a40 EFLAGS: 00010246
RAX: 0058 RBX: ff4d61160b74b880 RCX: ff4d61255e1fffa8
RDX:  RSI: fffe RDI: 9fd34f20
RBP: ff4d611d8e245c00 R08:  R09: ff5ad434865b3888
R10: ff5ad434865b3880 R11: ff4d61257fdc6fe8 R12: ff4d61160b74b8a0
R13: ff4d61160b74b8a0 R14: ff4d611d8e245c10 R15: ff4d611d8001ba70
FS:  () GS:ff4d611d5ea0() knlGS:
CS:  0010 DS:  ES:  CR0: 80050033
CR2: ff4d611fa1401000 CR3: 000aa0210001 CR4: 00771ef0
DR0:  DR1:  DR2: 
DR3:  DR6: fffe07f0 DR7: 0400
PKRU: 5554
Call Trace:
 
 intel_pasid_alloc_table+0x9c/0x1d0
 dmar_insert_one_dev_info+0x423/0x540
 ? device_to_iommu+0x12d/0x2f0
 intel_iommu_attach_device+0x116/0x290
 __iommu_attach_device+0x1a/0x90
 iommu_group_add_device+0x190/0x2c0
 __iommu_probe_device+0x13e/0x250
 iommu_probe_device+0x24/0x150
 iommu_bus_notifier+0x69/0x90
 blocking_notifier_call_chain+0x5a/0x80
 device_add+0x3db/0x7b0
 ? arch_memremap_can_ram_remap+0x19/0x50
 ? memremap+0x75/0x140
 pci_device_add+0x193/0x1d0
 pci_scan_single_device+0xb9/0xf0
 pci_scan_slot+0x4c/0x110
 pci_scan_child_bus_extend+0x3a/0x290
 vmd_enable_domain.constprop.0+0x63e/0x820
 vmd_probe+0x163/0x190
 local_pci_probe+0x42/0x80
 work_for_cpu_fn+0x13/0x20
 process_one_work+0x1e2/0x3b0
 worker_thread+0x1c4/0x3a0
 ? rescuer_thread+0x370/0x370
 kthread+0xc7/0xf0
 ? kthread_complete_and_exit+0x20/0x20
 ret_from_fork+0x1f/0x30
 
Modules linked in:
---[ end trace  ]---
...
Kernel panic - not syncing: Fatal exception
Kernel Offset: 0x1ca0 from 0x8100 (relocation range: 
0x8000-0xbfff)
---[ end Kernel panic - not syncing: Fatal exception ]---

The following 'lspci' output shows devices '1:80:*' are subdevices of
the VMD device :59:00.5:

  $ lspci
  ...
  :59:00.5 RAID bus controller: Intel Corporation Volume Management Device 
NVMe RAID Controller (rev 20)
  ...
  1:80:01.0 PCI bridge: Intel Corporation Device 352a (rev 03)
  1:80:03.0 PCI bridge: Intel Corporation Device 352b (rev 03)
  1:80:05.0 PCI bridge: Intel Corporation Device 352c (rev 03)
  1:80:07.0 PCI bridge: Intel Corporation Device 352d (rev 03)
  1:81:00.0 Non-Volatile memory controller: Intel Corporation NVMe 
Datacenter SSD [3DNAND, Beta Rock Controller]
  1:82:00.0 Non-Volatile memory controller: Intel Corporation NVMe 
Datacenter SSD [3DNAND, Beta Rock Controller]

The symptom 'list_add double add' is caused by the following failure
message:

  pci 1:80:01.0: DMAR: Setup RID2PASID failed
  pci 1:80:01.0: Failed to add to iommu group 42: -16
  pci 1:80:03.0: [8086:352b] type 01 class 0x060400

Device 1:80:01.0 is the subdevice of the VMD device :59:00.5,
so invoking intel_pasid_alloc_table() gets the pasid_table of the VMD
device :59:00.5. Here is call path:

  intel_pasid_alloc_table
pci_for_each_dma_alias
 get_alias_pasid_table
   search_pasid_table

pci_real_dma_dev() in pci_for_each_dma_alias() gets the real dma device
which is the VMD device :59:00.5. However, pte of the VMD device
:59:00.5 has been configured during this message "pci :59:00.5:
Adding to iommu group 42". So, the status -EBUSY is returned when
configuring pasid entry for device 1:80:01.0.

It then invokes dmar_remove_one_dev_info() to release
'struct device_dom

[PATCH] iommu/vt-d: Fix list_add double add when enabling VMD and scalable mode

2022-02-10 Thread Adrian Huang
From: Adrian Huang 

When enabling VMD and IOMMU scalable mode, the following kernel panic
call trace/kernel log is shown in Eagle Stream platform (Sapphire Rapids
CPU) during booting:

pci :59:00.5: Adding to iommu group 42
...
vmd :59:00.5: PCI host bridge to bus 1:80
pci 1:80:01.0: [8086:352a] type 01 class 0x060400
pci 1:80:01.0: reg 0x10: [mem 0x-0x0001 64bit]
pci 1:80:01.0: enabling Extended Tags
pci 1:80:01.0: PME# supported from D0 D3hot D3cold
pci 1:80:01.0: DMAR: Setup RID2PASID failed
pci 1:80:01.0: Failed to add to iommu group 42: -16
pci 1:80:03.0: [8086:352b] type 01 class 0x060400
pci 1:80:03.0: reg 0x10: [mem 0x-0x0001 64bit]
pci 1:80:03.0: enabling Extended Tags
pci 1:80:03.0: PME# supported from D0 D3hot D3cold
list_add double add: new=ff4d61160b74b8a0, prev=ff4d611d8e245c10, 
next=ff4d61160b74b8a0.
[ cut here ]
kernel BUG at lib/list_debug.c:29!
invalid opcode:  [#1] PREEMPT SMP NOPTI
CPU: 0 PID: 7 Comm: kworker/0:1 Not tainted 5.17.0-rc3+ #7
Hardware name: Lenovo ThinkSystem SR650V3/SB27A86647, BIOS ESE101Y-1.00 
01/13/2022
Workqueue: events work_for_cpu_fn
RIP: 0010:__list_add_valid.cold+0x26/0x3f
Code: 9a 4a ab ff 4c 89 c1 48 c7 c7 40 0c d9 9e e8 b9 b1 fe ff 0f 0b 48 89 f2 
4c 89 c1 48 89 fe 48 c7 c7 f0 0c d9 9e e8 a2 b1 fe ff <0f> 0b 48 89 d1 4c 89 c6 
4c 89 ca 48 c7 c7 98 0c d9 9e e8 8b b1 fe
RSP: :ff5ad434865b3a40 EFLAGS: 00010246
RAX: 0058 RBX: ff4d61160b74b880 RCX: ff4d61255e1fffa8
RDX:  RSI: fffe RDI: 9fd34f20
RBP: ff4d611d8e245c00 R08:  R09: ff5ad434865b3888
R10: ff5ad434865b3880 R11: ff4d61257fdc6fe8 R12: ff4d61160b74b8a0
R13: ff4d61160b74b8a0 R14: ff4d611d8e245c10 R15: ff4d611d8001ba70
FS:  () GS:ff4d611d5ea0() knlGS:
CS:  0010 DS:  ES:  CR0: 80050033
CR2: ff4d611fa1401000 CR3: 000aa0210001 CR4: 00771ef0
DR0:  DR1:  DR2: 
DR3:  DR6: fffe07f0 DR7: 0400
PKRU: 5554
Call Trace:
 
 intel_pasid_alloc_table+0x9c/0x1d0
 dmar_insert_one_dev_info+0x423/0x540
 ? device_to_iommu+0x12d/0x2f0
 intel_iommu_attach_device+0x116/0x290
 __iommu_attach_device+0x1a/0x90
 iommu_group_add_device+0x190/0x2c0
 __iommu_probe_device+0x13e/0x250
 iommu_probe_device+0x24/0x150
 iommu_bus_notifier+0x69/0x90
 blocking_notifier_call_chain+0x5a/0x80
 device_add+0x3db/0x7b0
 ? arch_memremap_can_ram_remap+0x19/0x50
 ? memremap+0x75/0x140
 pci_device_add+0x193/0x1d0
 pci_scan_single_device+0xb9/0xf0
 pci_scan_slot+0x4c/0x110
 pci_scan_child_bus_extend+0x3a/0x290
 vmd_enable_domain.constprop.0+0x63e/0x820
 vmd_probe+0x163/0x190
 local_pci_probe+0x42/0x80
 work_for_cpu_fn+0x13/0x20
 process_one_work+0x1e2/0x3b0
 worker_thread+0x1c4/0x3a0
 ? rescuer_thread+0x370/0x370
 kthread+0xc7/0xf0
 ? kthread_complete_and_exit+0x20/0x20
 ret_from_fork+0x1f/0x30
 
Modules linked in:
---[ end trace  ]---
...
Kernel panic - not syncing: Fatal exception
Kernel Offset: 0x1ca0 from 0x8100 (relocation range: 
0x8000-0xbfff)
---[ end Kernel panic - not syncing: Fatal exception ]---

The following 'lspci' output shows devices '1:80:*' are subdevices of
the VMD device :59:00.5:

  $ lspci
  ...
  :59:00.5 RAID bus controller: Intel Corporation Volume Management Device 
NVMe RAID Controller (rev 20)
  ...
  1:80:01.0 PCI bridge: Intel Corporation Device 352a (rev 03)
  1:80:03.0 PCI bridge: Intel Corporation Device 352b (rev 03)
  1:80:05.0 PCI bridge: Intel Corporation Device 352c (rev 03)
  1:80:07.0 PCI bridge: Intel Corporation Device 352d (rev 03)
  1:81:00.0 Non-Volatile memory controller: Intel Corporation NVMe 
Datacenter SSD [3DNAND, Beta Rock Controller]
  1:82:00.0 Non-Volatile memory controller: Intel Corporation NVMe 
Datacenter SSD [3DNAND, Beta Rock Controller]

The symptom 'list_add double add' is caused by the following failure
message:

  pci 1:80:01.0: DMAR: Setup RID2PASID failed
  pci 1:80:01.0: Failed to add to iommu group 42: -16
  pci 1:80:03.0: [8086:352b] type 01 class 0x060400

Device 1:80:01.0 is the subdevice of the VMD device :59:00.5,
so invoking intel_pasid_alloc_table() gets the pasid_table of the VMD
device :59:00.5. Here is call path:

  intel_pasid_alloc_table
pci_for_each_dma_alias
 get_alias_pasid_table
   search_pasid_table

pci_real_dma_dev() in pci_for_each_dma_alias() gets the real dma device
which is the VMD device :59:00.5. However, pte of the VMD device
:59:00.5 has been configured during this message "pci :59:00.5:
Adding to iommu group 42". So, the status -EBUSY is returned when
configuring pasid entry for device 1:80:01.0.

It then invokes dmar_remove_one_dev_info() to release
'struct device_dom

[RESEND PATCH 1/1] iommu/amd: Remove unnecessary assignment

2020-12-09 Thread Adrian Huang
From: Adrian Huang 

From: Adrian Huang 

The values of local variables are assigned after local variables
are declared, so no need to assign the initial value during the
variable declaration.

And, no need to assign NULL for the local variable 'ivrs_base'
after invoking acpi_put_table().

Signed-off-by: Adrian Huang 
---
 drivers/iommu/amd/init.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c
index 23a790f8f550..103cbf8fc2d9 100644
--- a/drivers/iommu/amd/init.c
+++ b/drivers/iommu/amd/init.c
@@ -1916,7 +1916,7 @@ static void print_iommu_info(void)
 static int __init amd_iommu_init_pci(void)
 {
struct amd_iommu *iommu;
-   int ret = 0;
+   int ret;
 
for_each_iommu(iommu) {
ret = iommu_init_pci(iommu);
@@ -2555,8 +2555,8 @@ static void __init free_dma_resources(void)
 static int __init early_amd_iommu_init(void)
 {
struct acpi_table_header *ivrs_base;
+   int i, remap_cache_sz, ret;
acpi_status status;
-   int i, remap_cache_sz, ret = 0;
u32 pci_id;
 
if (!amd_iommu_detected)
@@ -2698,7 +2698,6 @@ static int __init early_amd_iommu_init(void)
 out:
/* Don't leak any ACPI memory */
acpi_put_table(ivrs_base);
-   ivrs_base = NULL;
 
return ret;
 }
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 1/1] iommu/amd: Remove unnecessary assignment

2020-10-12 Thread Adrian Huang
From: Adrian Huang 

The values of local variables are assigned after local variables
are declared, so no need to assign the initial value during the
variable declaration.

And, no need to assign NULL for the local variable 'ivrs_base' 
after invoking acpi_put_table().

Signed-off-by: Adrian Huang 
---
 drivers/iommu/amd/init.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c
index 1ba6b4cc56e8..f171078f7ea0 100644
--- a/drivers/iommu/amd/init.c
+++ b/drivers/iommu/amd/init.c
@@ -1858,7 +1858,7 @@ static void print_iommu_info(void)
 static int __init amd_iommu_init_pci(void)
 {
struct amd_iommu *iommu;
-   int ret = 0;
+   int ret;
 
for_each_iommu(iommu) {
ret = iommu_init_pci(iommu);
@@ -2494,8 +2494,8 @@ static void __init free_dma_resources(void)
 static int __init early_amd_iommu_init(void)
 {
struct acpi_table_header *ivrs_base;
+   int i, remap_cache_sz, ret;
acpi_status status;
-   int i, remap_cache_sz, ret = 0;
u32 pci_id;
 
if (!amd_iommu_detected)
@@ -2637,7 +2637,6 @@ static int __init early_amd_iommu_init(void)
 out:
/* Don't leak any ACPI memory */
acpi_put_table(ivrs_base);
-   ivrs_base = NULL;
 
return ret;
 }
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 1/1] iommu/amd: Fix the overwritten field in IVMD header

2020-09-26 Thread Adrian Huang
From: Adrian Huang 

Commit 387caf0b759a ("iommu/amd: Treat per-device exclusion
ranges as r/w unity-mapped regions") accidentally overwrites
the 'flags' field in IVMD (struct ivmd_header) when the I/O
virtualization memory definition is associated with the
exclusion range entry. This leads to the corrupted IVMD table
(incorrect checksum). The kdump kernel reports the invalid checksum:

ACPI BIOS Warning (bug): Incorrect checksum in table [IVRS] - 0x5C, should be 
0x60 (20200717/tbprint-177)
AMD-Vi: [Firmware Bug]: IVRS invalid checksum

Fix the above-mentioned issue by modifying the 'struct unity_map_entry'
member instead of the IVMD header.

Cleanup: The *exclusion_range* functions are not used anymore, so
get rid of them.

Fixes: 387caf0b759a ("iommu/amd: Treat per-device exclusion ranges as r/w 
unity-mapped regions")
Reported-and-tested-by: Baoquan He 
Signed-off-by: Adrian Huang 
Cc: Jerry Snitselaar 
---
 drivers/iommu/amd/init.c | 56 +++-
 1 file changed, 10 insertions(+), 46 deletions(-)

diff --git a/drivers/iommu/amd/init.c b/drivers/iommu/amd/init.c
index 445a08d23fed..1ba6b4cc56e8 100644
--- a/drivers/iommu/amd/init.c
+++ b/drivers/iommu/amd/init.c
@@ -1103,25 +1103,6 @@ static int __init add_early_maps(void)
return 0;
 }
 
-/*
- * Reads the device exclusion range from ACPI and initializes the IOMMU with
- * it
- */
-static void __init set_device_exclusion_range(u16 devid, struct ivmd_header *m)
-{
-   if (!(m->flags & IVMD_FLAG_EXCL_RANGE))
-   return;
-
-   /*
-* Treat per-device exclusion ranges as r/w unity-mapped regions
-* since some buggy BIOSes might lead to the overwritten exclusion
-* range (exclusion_start and exclusion_length members). This
-* happens when there are multiple exclusion ranges (IVMD entries)
-* defined in ACPI table.
-*/
-   m->flags = (IVMD_FLAG_IW | IVMD_FLAG_IR | IVMD_FLAG_UNITY_MAP);
-}
-
 /*
  * Takes a pointer to an AMD IOMMU entry in the ACPI table and
  * initializes the hardware and our data structures with it.
@@ -2073,30 +2054,6 @@ static void __init free_unity_maps(void)
}
 }
 
-/* called when we find an exclusion range definition in ACPI */
-static int __init init_exclusion_range(struct ivmd_header *m)
-{
-   int i;
-
-   switch (m->type) {
-   case ACPI_IVMD_TYPE:
-   set_device_exclusion_range(m->devid, m);
-   break;
-   case ACPI_IVMD_TYPE_ALL:
-   for (i = 0; i <= amd_iommu_last_bdf; ++i)
-   set_device_exclusion_range(i, m);
-   break;
-   case ACPI_IVMD_TYPE_RANGE:
-   for (i = m->devid; i <= m->aux; ++i)
-   set_device_exclusion_range(i, m);
-   break;
-   default:
-   break;
-   }
-
-   return 0;
-}
-
 /* called for unity map ACPI definition */
 static int __init init_unity_map_range(struct ivmd_header *m)
 {
@@ -2107,9 +2064,6 @@ static int __init init_unity_map_range(struct ivmd_header 
*m)
if (e == NULL)
return -ENOMEM;
 
-   if (m->flags & IVMD_FLAG_EXCL_RANGE)
-   init_exclusion_range(m);
-
switch (m->type) {
default:
kfree(e);
@@ -2133,6 +2087,16 @@ static int __init init_unity_map_range(struct 
ivmd_header *m)
e->address_end = e->address_start + PAGE_ALIGN(m->range_length);
e->prot = m->flags >> 1;
 
+   /*
+* Treat per-device exclusion ranges as r/w unity-mapped regions
+* since some buggy BIOSes might lead to the overwritten exclusion
+* range (exclusion_start and exclusion_length members). This
+* happens when there are multiple exclusion ranges (IVMD entries)
+* defined in ACPI table.
+*/
+   if (m->flags & IVMD_FLAG_EXCL_RANGE)
+   e->prot = (IVMD_FLAG_IW | IVMD_FLAG_IR) >> 1;
+
DUMP_printk("%s devid_start: %02x:%02x.%x devid_end: %02x:%02x.%x"
" range_start: %016llx range_end: %016llx flags: %x\n", s,
PCI_BUS_NUM(e->devid_start), PCI_SLOT(e->devid_start),
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 1/1] iommu/amd: Fix the configuration of GCR3 table root pointer

2020-02-14 Thread Adrian Huang
From: Adrian Huang 

The SPA of the GCR3 table root pointer[51:31] masks 20 bits. However,
this requires 21 bits (Please see the AMD IOMMU specification).
This leads to the potential failure when the bit 51 of SPA of
the GCR3 table root pointer is 1'.

Signed-off-by: Adrian Huang 
---
 drivers/iommu/amd_iommu_types.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
index f8d01d6b00da..ca8c4522045b 100644
--- a/drivers/iommu/amd_iommu_types.h
+++ b/drivers/iommu/amd_iommu_types.h
@@ -348,7 +348,7 @@
 
 #define DTE_GCR3_VAL_A(x)  (((x) >> 12) & 0x7ULL)
 #define DTE_GCR3_VAL_B(x)  (((x) >> 15) & 0x0ULL)
-#define DTE_GCR3_VAL_C(x)  (((x) >> 31) & 0xfULL)
+#define DTE_GCR3_VAL_C(x)  (((x) >> 31) & 0x1fULL)
 
 #define DTE_GCR3_INDEX_A   0
 #define DTE_GCR3_INDEX_B   1
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 1/1] iommu/amd: Remove the unnecessary assignment

2020-01-22 Thread Adrian Huang
From: Adrian Huang 

The assignment of the global variable 'iommu_detected' has been
moved from amd_iommu_init_dma_ops() to amd_iommu_detect(), so
this patch removes the assignment in amd_iommu_init_dma_ops().

Signed-off-by: Adrian Huang 
---
 drivers/iommu/amd_iommu.c | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/iommu/amd_iommu.c b/drivers/iommu/amd_iommu.c
index bd25674ee4db..79f08c0a1f00 100644
--- a/drivers/iommu/amd_iommu.c
+++ b/drivers/iommu/amd_iommu.c
@@ -2297,7 +2297,6 @@ int __init amd_iommu_init_api(void)
 int __init amd_iommu_init_dma_ops(void)
 {
swiotlb= (iommu_default_passthrough() || sme_me_mask) ? 1 : 0;
-   iommu_detected = 1;
 
if (amd_iommu_unmap_flush)
pr_info("IO/TLB flush on unmap enabled\n");
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 0/2] iommu/amd: Optimization and cleanup for unused variables

2020-01-08 Thread Adrian Huang
This series optimizes the register reading by using readq instead of
readl and cleans up the unused variables.

Adrian Huang (2):
  iommu/amd: Replace two consecutive readl calls with one readq
  iommu/amd: Remove unused struct member

 drivers/iommu/amd_iommu_init.c  | 6 +-
 drivers/iommu/amd_iommu_types.h | 1 -
 2 files changed, 1 insertion(+), 6 deletions(-)

-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 1/2] iommu/amd: Replace two consecutive readl calls with one readq

2020-01-08 Thread Adrian Huang
From: Adrian Huang 

Optimize the reigster reading by using readq instead of the two
consecutive readl calls.

Signed-off-by: Adrian Huang 
---
 drivers/iommu/amd_iommu_init.c | 6 +-
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index c5167fe0bd5f..cfdc4b60ccbe 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -1718,7 +1718,6 @@ static const struct attribute_group *amd_iommu_groups[] = 
{
 static int __init iommu_init_pci(struct amd_iommu *iommu)
 {
int cap_ptr = iommu->cap_ptr;
-   u32 low, high;
int ret;
 
iommu->dev = pci_get_domain_bus_and_slot(0, PCI_BUS_NUM(iommu->devid),
@@ -1736,10 +1735,7 @@ static int __init iommu_init_pci(struct amd_iommu *iommu)
amd_iommu_iotlb_sup = false;
 
/* read extended feature bits */
-   low  = readl(iommu->mmio_base + MMIO_EXT_FEATURES);
-   high = readl(iommu->mmio_base + MMIO_EXT_FEATURES + 4);
-
-   iommu->features = ((u64)high << 32) | low;
+   iommu->features = readq(iommu->mmio_base + MMIO_EXT_FEATURES);
 
if (iommu_feature(iommu, FEATURE_GT)) {
int glxval;
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 2/2] iommu/amd: Remove unused struct member

2020-01-08 Thread Adrian Huang
From: Adrian Huang 

Commit c805b428f206 ("iommu/amd: Remove amd_iommu_pd_list") removes
the global list for the allocated protection domains. The
corresponding member 'list' of the protection_domain struct is
not used anymore, so it can be removed.

Signed-off-by: Adrian Huang 
---
 drivers/iommu/amd_iommu_types.h | 1 -
 1 file changed, 1 deletion(-)

diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
index 500f0b78879d..f8d01d6b00da 100644
--- a/drivers/iommu/amd_iommu_types.h
+++ b/drivers/iommu/amd_iommu_types.h
@@ -463,7 +463,6 @@ struct amd_irte_ops;
  * independent of their use.
  */
 struct protection_domain {
-   struct list_head list;  /* for list of all protection domains */
struct list_head dev_list; /* List of all devices in this domain */
struct iommu_domain domain; /* generic domain handle used by
   iommu core code */
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 1/1] iommu/amd: Fix typos for PPR macros

2019-12-29 Thread Adrian Huang
From: Adrian Huang 

The bit 13 and bit 14 of the IOMMU control register are
PPRLogEn and PPRIntEn. They are related to PPR (Peripheral Page
Request) instead of 'PPF'. Fix them accrodingly.

Signed-off-by: Adrian Huang 
---
 drivers/iommu/amd_iommu_init.c  | 4 ++--
 drivers/iommu/amd_iommu_types.h | 4 ++--
 2 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index ba7ee4aa04f9..6e82aa606e2b 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -716,7 +716,7 @@ static void iommu_enable_ppr_log(struct amd_iommu *iommu)
writel(0x00, iommu->mmio_base + MMIO_PPR_HEAD_OFFSET);
writel(0x00, iommu->mmio_base + MMIO_PPR_TAIL_OFFSET);
 
-   iommu_feature_enable(iommu, CONTROL_PPFLOG_EN);
+   iommu_feature_enable(iommu, CONTROL_PPRLOG_EN);
iommu_feature_enable(iommu, CONTROL_PPR_EN);
 }
 
@@ -2035,7 +2035,7 @@ static int iommu_init_msi(struct amd_iommu *iommu)
iommu_feature_enable(iommu, CONTROL_EVT_INT_EN);
 
if (iommu->ppr_log != NULL)
-   iommu_feature_enable(iommu, CONTROL_PPFINT_EN);
+   iommu_feature_enable(iommu, CONTROL_PPRINT_EN);
 
iommu_ga_log_enable(iommu);
 
diff --git a/drivers/iommu/amd_iommu_types.h b/drivers/iommu/amd_iommu_types.h
index 798e1533a147..500f0b78879d 100644
--- a/drivers/iommu/amd_iommu_types.h
+++ b/drivers/iommu/amd_iommu_types.h
@@ -147,8 +147,8 @@
 #define CONTROL_COHERENT_EN 0x0aULL
 #define CONTROL_ISOC_EN 0x0bULL
 #define CONTROL_CMDBUF_EN   0x0cULL
-#define CONTROL_PPFLOG_EN   0x0dULL
-#define CONTROL_PPFINT_EN   0x0eULL
+#define CONTROL_PPRLOG_EN   0x0dULL
+#define CONTROL_PPRINT_EN   0x0eULL
 #define CONTROL_PPR_EN  0x0fULL
 #define CONTROL_GT_EN   0x10ULL
 #define CONTROL_GA_EN   0x11ULL
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 1/1] iommu/amd: Remove local variables

2019-12-24 Thread Adrian Huang
From: Adrian Huang 

The usage of the local variables 'range' and 'misc' was removed
from commit 226e889b20a9 ("iommu/amd: Remove first/last_device handling")
and commit 23c742db2171 ("iommu/amd: Split out PCI related parts of
IOMMU initialization"). So, remove them accrodingly.

Signed-off-by: Adrian Huang 
---
 drivers/iommu/amd_iommu_init.c | 6 +-
 1 file changed, 1 insertion(+), 5 deletions(-)

diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index ba7ee4aa04f9..2cc7d61b1802 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -1718,7 +1718,7 @@ static const struct attribute_group *amd_iommu_groups[] = 
{
 static int __init iommu_init_pci(struct amd_iommu *iommu)
 {
int cap_ptr = iommu->cap_ptr;
-   u32 range, misc, low, high;
+   u32 low, high;
int ret;
 
iommu->dev = pci_get_domain_bus_and_slot(0, PCI_BUS_NUM(iommu->devid),
@@ -1731,10 +1731,6 @@ static int __init iommu_init_pci(struct amd_iommu *iommu)
 
pci_read_config_dword(iommu->dev, cap_ptr + MMIO_CAP_HDR_OFFSET,
  >cap);
-   pci_read_config_dword(iommu->dev, cap_ptr + MMIO_RANGE_OFFSET,
- );
-   pci_read_config_dword(iommu->dev, cap_ptr + MMIO_MISC_OFFSET,
- );
 
if (!(iommu->cap & (1 << IOMMU_CAP_IOTLB)))
amd_iommu_iotlb_sup = false;
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 1/1] iommu/amd: Treat per-device exclusion ranges as r/w unity-mapped regions

2019-11-13 Thread Adrian Huang
Some buggy BIOSes might define multiple exclusion ranges of the
IVMD entries which are associated with the same IOMMU hardware.
This leads to the overwritten exclusion range (exclusion_start
and exclusion_length members) in set_device_exclusion_range().

Here is a real case:
When attaching two Broadcom RAID controllers to a server, the first
one reports the failure during booting (the disks connecting to the
RAID controller cannot be detected).

This patch prevents the issue by treating per-device exclusion
ranges as r/w unity-mapped regions.

Discussion:
  * https://lists.linuxfoundation.org/pipermail/iommu/2019-November/040140.html

Suggested-by: Joerg Roedel 
Signed-off-by: Adrian Huang 
---
 drivers/iommu/amd_iommu_init.c | 20 ++--
 1 file changed, 10 insertions(+), 10 deletions(-)

diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index 568c52317757..1b65302e07b1 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -71,6 +71,8 @@
 #define IVHD_FLAG_ISOC_EN_MASK  0x08
 
 #define IVMD_FLAG_EXCL_RANGE0x08
+#define IVMD_FLAG_IW0x04
+#define IVMD_FLAG_IR0x02
 #define IVMD_FLAG_UNITY_MAP 0x01
 
 #define ACPI_DEVFLAG_INITPASS   0x01
@@ -1121,16 +1123,14 @@ static void __init set_device_exclusion_range(u16 
devid, struct ivmd_header *m)
if (!(m->flags & IVMD_FLAG_EXCL_RANGE))
return;
 
-   if (iommu) {
-   /*
-* We only can configure exclusion ranges per IOMMU, not
-* per device. But we can enable the exclusion range per
-* device. This is done here
-*/
-   set_dev_entry_bit(devid, DEV_ENTRY_EX);
-   iommu->exclusion_start = m->range_start;
-   iommu->exclusion_length = m->range_length;
-   }
+   /*
+* Treat per-device exclusion ranges as r/w unity-mapped regions
+* since some buggy BIOSes might lead to the overwritten exclusion
+* range (exclusion_start and exclusion_length members). This
+* happens when there are multiple exclusion ranges (IVMD entries)
+* defined in ACPI table.
+*/
+   m->flags = (IVMD_FLAG_IW | IVMD_FLAG_IR | IVMD_FLAG_UNITY_MAP);
 }
 
 /*
-- 
2.17.1

___
iommu mailing list
iommu@lists.linux-foundation.org
https://lists.linuxfoundation.org/mailman/listinfo/iommu


[PATCH 1/1] iommu/amd: Fix the overwritten exclusion range with multiple IVMDs

2019-11-04 Thread Adrian Huang
When attaching two Broadcom RAID controllers to a server, the first one
reports the failure during booting (the disks connecting to the RAID
controller cannot be detected):

  megaraid_sas :42:00.0: Init cmd return status FAILED for SCSI host 0
  megaraid_sas :42:00.0: Failed from megasas_init_fw 6376

Root-cause of the issue:
1) Those two RAID controllers define their own IVMDs with the
   valid exclusion range, and they are associated with the same
   IOMMU hardware:

 Subtable Type : 10 [Hardware Definition Block]
 Flags : B0
Length : 0028
  DeviceId : 4002

 Capability Offset : 0040
  Base Address : B410
 PCI Segment Group : 
   Virtualization Info : 
  Reserved : 80048F6F

Entry Type : 03
 Device ID : 4008
  Data Setting : 00

Entry Type : 04
 Device ID : 7FFE
  Data Setting : 00

 Subtable Type : 21 [Memory Definition Block]
 Flags : 08
Length : 0020
  DeviceId : 4200

Auxiliary Data : 
  Reserved : 
 Start Address : 9F58D000
 Memory Length : 0804

 Subtable Type : 21 [Memory Definition Block]
 Flags : 08
Length : 0020
  DeviceId : 4300

Auxiliary Data : 
  Reserved : 
 Start Address : 9754D000
 Memory Length : 0804

2) When set_device_exclusion_range() parses the IVMD of devce id
   '4200', the exclusion range of the amd_iommu struct becomes:

iommu->exclusion_start = 0x9F58D000;
iommu->exclusion_length = 0x804;

3) When parsing the second IVMD (device id '4300') in
   set_device_exclusion_range(), the exclusion range of the
   amd_iommu struct becomes:

iommu->exclusion_start = 0x9754D000;
iommu->exclusion_length = 0x804;

   This overwrites the first IVMD configuration, which leads to
   the failure of the first RAID controller.

This patch fixes the issue by using unity map for multiple IVMDs if
those IVMDs define the valid exclusion range (different exclusion range)
and they are associated with the same IOMMU hardware. Note that the first
IVMD still uses the exclusion range.

Signed-off-by: Adrian Huang 
---
 drivers/iommu/amd_iommu_init.c | 33 +++--
 1 file changed, 31 insertions(+), 2 deletions(-)

diff --git a/drivers/iommu/amd_iommu_init.c b/drivers/iommu/amd_iommu_init.c
index 568c52317757..d65b548a42f5 100644
--- a/drivers/iommu/amd_iommu_init.c
+++ b/drivers/iommu/amd_iommu_init.c
@@ -71,6 +71,8 @@
 #define IVHD_FLAG_ISOC_EN_MASK  0x08
 
 #define IVMD_FLAG_EXCL_RANGE0x08
+#define IVMD_FLAG_IW0x04
+#define IVMD_FLAG_IR0x02
 #define IVMD_FLAG_UNITY_MAP 0x01
 
 #define ACPI_DEVFLAG_INITPASS   0x01
@@ -1110,6 +1112,32 @@ static int __init add_early_maps(void)
return 0;
 }
 
+static int __init exclusion_range_has_configured(struct amd_iommu *iommu,
+   struct ivmd_header *m)
+{
+   /* Not configure yet. */
+   if (!iommu->exclusion_start) {
+   iommu->exclusion_start = m->range_start;
+   iommu->exclusion_length = m->range_length;
+
+   return 0;
+   }
+
+   if (iommu->exclusion_start == m->range_start &&
+   iommu->exclusion_length == m->range_length)
+   return 0;
+
+   /*
+* The exclusion range of the iommu has been configured
+* by the other IVMD, so we need to use unity map for this
+* IVMD to avoid the overwritten exclusion range members of the
+* amd_iommu structure.
+*/
+   m->flags = (IVMD_FLAG_IW | IVMD_FLAG_IR | IVMD_FLAG_UNITY_MAP);
+
+   return 1;
+}
+
 /*
  * Reads the device exclusion range from ACPI and initializes the IOMMU with
  * it
@@ -1122,14 +1150,15 @@ static void __init set_device_exclusion_range(u16 
devid, struct ivmd_header *m)
return;
 
if (iommu) {
+   if (exclusion_range_has_configured(iommu, m))
+   return;
+
/*
 * We only can configure exclusion ranges per IOMMU, not
 * per device. But we can enable the exclusion range per
 * device. This is done here
 */