Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 75f1cdf1 authored by FUJITA Tomonori's avatar FUJITA Tomonori Committed by Ingo Molnar
Browse files

x86: Handle HW IOMMU initialization failure gracefully

If HW IOMMU initialization fails (Intel VT-d often does this,
typically due to BIOS bugs), we fall back to nommu. It doesn't
work for the majority since nowadays we have more than 4GB
memory so we must use swiotlb instead of nommu.

The problem is that it's too late to initialize swiotlb when HW
IOMMU initialization fails. We need to allocate swiotlb memory
earlier from bootmem allocator. Chris explained the issue in
detail:

  http://marc.info/?l=linux-kernel&m=125657444317079&w=2



The current x86 IOMMU initialization sequence is too complicated
and handling the above issue makes it more hacky.

This patch changes x86 IOMMU initialization sequence to handle
the above issue cleanly.

The new x86 IOMMU initialization sequence are:

1. we initialize the swiotlb (and setting swiotlb to 1) in the case
   of (max_pfn > MAX_DMA32_PFN && !no_iommu). dma_ops is set to
   swiotlb_dma_ops or nommu_dma_ops. if swiotlb usage is forced by
   the boot option, we finish here.

2. we call the detection functions of all the IOMMUs

3. the detection function sets x86_init.iommu.iommu_init to the
   IOMMU initialization function (so we can avoid calling the
   initialization functions of all the IOMMUs needlessly).

4. if the IOMMU initialization function doesn't need to swiotlb
   then sets swiotlb to zero (e.g. the initialization is
   sucessful).

5. if we find that swiotlb is set to zero, we free swiotlb
   resource.

Signed-off-by: default avatarFUJITA Tomonori <fujita.tomonori@lab.ntt.co.jp>
Cc: chrisw@sous-sol.org
Cc: dwmw2@infradead.org
Cc: joerg.roedel@amd.com
Cc: muli@il.ibm.com
LKML-Reference: <1257849980-22640-10-git-send-email-fujita.tomonori@lab.ntt.co.jp>
Signed-off-by: default avatarIngo Molnar <mingo@elte.hu>
parent ad32e8cb
Loading
Loading
Loading
Loading
+0 −1
Original line number Diff line number Diff line
@@ -2,7 +2,6 @@
#define _ASM_X86_IOMMU_H

static inline void iommu_shutdown_noop(void) {}
extern void no_iommu_init(void);
extern struct dma_map_ops nommu_dma_ops;
extern int force_iommu, no_iommu;
extern int iommu_detected;
+1 −1
Original line number Diff line number Diff line
@@ -2110,8 +2110,8 @@ int __init amd_iommu_init_dma_ops(void)
		prealloc_protection_domains();

	iommu_detected = 1;
	force_iommu = 1;
	bad_dma_address = 0;
	swiotlb = 0;
#ifdef CONFIG_GART_IOMMU
	gart_iommu_aperture_disabled = 1;
	gart_iommu_aperture = 0;
+1 −1
Original line number Diff line number Diff line
@@ -1330,7 +1330,7 @@ static int __init early_amd_iommu_detect(struct acpi_table_header *table)

void __init amd_iommu_detect(void)
{
	if (swiotlb || no_iommu || (iommu_detected && !gart_iommu_aperture))
	if (no_iommu || (iommu_detected && !gart_iommu_aperture))
		return;

	if (acpi_table_parse("IVRS", early_amd_iommu_detect) == 0) {
+1 −1
Original line number Diff line number Diff line
@@ -458,7 +458,7 @@ void __init gart_iommu_hole_init(void)

	if (aper_alloc) {
		/* Got the aperture from the AGP bridge */
	} else if (swiotlb && !valid_agp) {
	} else if (!valid_agp) {
		/* Do nothing */
	} else if ((!no_iommu && max_pfn > MAX_DMA32_PFN) ||
		   force_iommu ||
+1 −9
Original line number Diff line number Diff line
@@ -1360,7 +1360,7 @@ void __init detect_calgary(void)
	 * if the user specified iommu=off or iommu=soft or we found
	 * another HW IOMMU already, bail out.
	 */
	if (swiotlb || no_iommu || iommu_detected)
	if (no_iommu || iommu_detected)
		return;

	if (!use_calgary)
@@ -1445,10 +1445,6 @@ void __init detect_calgary(void)
		printk(KERN_INFO "PCI-DMA: Calgary TCE table spec is %d\n",
		       specified_table_size);

		/* swiotlb for devices that aren't behind the Calgary. */
		if (max_pfn > MAX_DMA32_PFN)
			swiotlb = 1;

		x86_init.iommu.iommu_init = calgary_iommu_init;
	}
	return;
@@ -1476,11 +1472,7 @@ int __init calgary_iommu_init(void)
		return ret;
	}

	force_iommu = 1;
	bad_dma_address = 0x0;
	/* dma_ops is set to swiotlb or nommu */
	if (!dma_ops)
		dma_ops = &nommu_dma_ops;

	return 0;
}
Loading