Subject: [PATCH] x86: Don't enable swiotlb if there is not enough ram for it Normal boot path on system with iommu support: swiotlb buffer will be allocated early at first and then try to initialize iommu, if iommu for intel or amd could setup properly, swiotlb buffer will be freed. The early allocating is with bootmem, and get panic when we try to use kdump with buffer above 4G only if swiotlb is enabled. because actually the kernel can go on without swiotlb, and use intel iommu. Try disable swiotlb if there is not enough ram for it. That is for kdump to use kernel above 4G. -v2: Shuah Khan pointed out that AMD iommu unhandled devices that need swiotlb will have problem. In that case, we have to panic, because we do not enable swiotlb before. -v3: replace hard-code 1M low ram checking with probed low ram size that is needed by swiotlb. Suggested-by: Eric W. Biederman Signed-off-by: Yinghai Lu Cc: Konrad Rzeszutek Wilk Cc: Joerg Roedel --- arch/x86/kernel/pci-swiotlb.c | 15 +++++++++++---- drivers/iommu/amd_iommu.c | 4 ++++ include/linux/swiotlb.h | 1 + lib/swiotlb.c | 21 ++++++++++++++++++++- 4 files changed, 36 insertions(+), 5 deletions(-) Index: linux-2.6/arch/x86/kernel/pci-swiotlb.c =================================================================== --- linux-2.6.orig/arch/x86/kernel/pci-swiotlb.c +++ linux-2.6/arch/x86/kernel/pci-swiotlb.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -50,6 +51,12 @@ static struct dma_map_ops swiotlb_dma_op .dma_supported = NULL, }; +static bool __init enough_mem_for_swiotlb(void) +{ + /* do we have enough low ram ? */ + return memblock_mem_size(1ULL<<(32-PAGE_SHIFT)) > + low_ram_size_for_swiotlb(); +} /* * pci_swiotlb_detect_override - set swiotlb to 1 if necessary * @@ -58,12 +65,12 @@ static struct dma_map_ops swiotlb_dma_op */ int __init pci_swiotlb_detect_override(void) { - int use_swiotlb = swiotlb | swiotlb_force; - if (swiotlb_force) swiotlb = 1; + else if (!enough_mem_for_swiotlb()) + swiotlb = 0; - return use_swiotlb; + return swiotlb; } IOMMU_INIT_FINISH(pci_swiotlb_detect_override, pci_xen_swiotlb_detect, @@ -78,7 +85,7 @@ int __init pci_swiotlb_detect_4gb(void) { /* don't initialize swiotlb if iommu=off (no_iommu=1) */ #ifdef CONFIG_X86_64 - if (!no_iommu && max_pfn > MAX_DMA32_PFN) + if (!no_iommu && max_pfn > MAX_DMA32_PFN && enough_mem_for_swiotlb()) swiotlb = 1; #endif return swiotlb; Index: linux-2.6/drivers/iommu/amd_iommu.c =================================================================== --- linux-2.6.orig/drivers/iommu/amd_iommu.c +++ linux-2.6/drivers/iommu/amd_iommu.c @@ -3144,6 +3144,7 @@ int __init amd_iommu_init_dma_ops(void) { struct amd_iommu *iommu; int ret, unhandled; + int swiotlb_orig; /* * first allocate a default protection domain for every IOMMU we @@ -3166,12 +3167,15 @@ int __init amd_iommu_init_dma_ops(void) prealloc_protection_domains(); iommu_detected = 1; + swiotlb_orig = swiotlb; swiotlb = 0; /* Make the driver finally visible to the drivers */ unhandled = device_dma_ops_init(); if (unhandled && max_pfn > MAX_DMA32_PFN) { /* There are unhandled devices - initialize swiotlb for them */ + if (!swiotlb_orig) + panic("can not enable swiotlb for unhandled devices by AMD iommu!\n"); swiotlb = 1; } Index: linux-2.6/include/linux/swiotlb.h =================================================================== --- linux-2.6.orig/include/linux/swiotlb.h +++ linux-2.6/include/linux/swiotlb.h @@ -22,6 +22,7 @@ extern int swiotlb_force; */ #define IO_TLB_SHIFT 11 +unsigned long low_ram_size_for_swiotlb(void); extern void swiotlb_init(int verbose); extern void swiotlb_init_with_tbl(char *tlb, unsigned long nslabs, int verbose); extern unsigned long swiotlb_nr_tbl(void); Index: linux-2.6/lib/swiotlb.c =================================================================== --- linux-2.6.orig/lib/swiotlb.c +++ linux-2.6/lib/swiotlb.c @@ -198,10 +198,29 @@ swiotlb_init_with_default_size(size_t de swiotlb_init_with_tbl(vstart, io_tlb_nslabs, verbose); } +/* default to 64MB */ +#define SWIOTLB_DEFAULT_SIZE (64UL<<20) void __init swiotlb_init(int verbose) { - swiotlb_init_with_default_size(64 * (1<<20), verbose); /* default to 64MB */ + swiotlb_init_with_default_size(SWIOTLB_DEFAULT_SIZE, verbose); +} + +unsigned long __init low_ram_size_for_swiotlb(void) +{ + unsigned long bytes; + unsigned long nslabs = io_tlb_nslabs; + + if (!nslabs) { + nslabs = (SWIOTLB_DEFAULT_SIZE >> IO_TLB_SHIFT); + nslabs = ALIGN(nslabs, IO_TLB_SEGSIZE); + } + + bytes = PAGE_ALIGN(nslabs << IO_TLB_SHIFT); + + bytes += PAGE_ALIGN(io_tlb_overflow); + + return bytes; } /*