forked from Minki/linux
75f1cdf1dd
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: FUJITA 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: Ingo Molnar <mingo@elte.hu>
58 lines
1.5 KiB
C
58 lines
1.5 KiB
C
/* Glue code to lib/swiotlb.c */
|
|
|
|
#include <linux/pci.h>
|
|
#include <linux/cache.h>
|
|
#include <linux/module.h>
|
|
#include <linux/swiotlb.h>
|
|
#include <linux/bootmem.h>
|
|
#include <linux/dma-mapping.h>
|
|
|
|
#include <asm/iommu.h>
|
|
#include <asm/swiotlb.h>
|
|
#include <asm/dma.h>
|
|
|
|
int swiotlb __read_mostly;
|
|
|
|
static void *x86_swiotlb_alloc_coherent(struct device *hwdev, size_t size,
|
|
dma_addr_t *dma_handle, gfp_t flags)
|
|
{
|
|
void *vaddr;
|
|
|
|
vaddr = dma_generic_alloc_coherent(hwdev, size, dma_handle, flags);
|
|
if (vaddr)
|
|
return vaddr;
|
|
|
|
return swiotlb_alloc_coherent(hwdev, size, dma_handle, flags);
|
|
}
|
|
|
|
static struct dma_map_ops swiotlb_dma_ops = {
|
|
.mapping_error = swiotlb_dma_mapping_error,
|
|
.alloc_coherent = x86_swiotlb_alloc_coherent,
|
|
.free_coherent = swiotlb_free_coherent,
|
|
.sync_single_for_cpu = swiotlb_sync_single_for_cpu,
|
|
.sync_single_for_device = swiotlb_sync_single_for_device,
|
|
.sync_single_range_for_cpu = swiotlb_sync_single_range_for_cpu,
|
|
.sync_single_range_for_device = swiotlb_sync_single_range_for_device,
|
|
.sync_sg_for_cpu = swiotlb_sync_sg_for_cpu,
|
|
.sync_sg_for_device = swiotlb_sync_sg_for_device,
|
|
.map_sg = swiotlb_map_sg_attrs,
|
|
.unmap_sg = swiotlb_unmap_sg_attrs,
|
|
.map_page = swiotlb_map_page,
|
|
.unmap_page = swiotlb_unmap_page,
|
|
.dma_supported = NULL,
|
|
};
|
|
|
|
void __init pci_swiotlb_init(void)
|
|
{
|
|
/* don't initialize swiotlb if iommu=off (no_iommu=1) */
|
|
#ifdef CONFIG_X86_64
|
|
if (!no_iommu && max_pfn > MAX_DMA32_PFN)
|
|
swiotlb = 1;
|
|
#endif
|
|
if (swiotlb) {
|
|
swiotlb_init(0);
|
|
dma_ops = &swiotlb_dma_ops;
|
|
} else
|
|
dma_ops = &nommu_dma_ops;
|
|
}
|