Subject: [PATCH] x86: Don't panic if can not alloc buffer for swiotlb 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 could panic when we try to use kdump with buffer above 4G only, or with memmap to limit mem under 4G. According to Eric, add _nopanic version and no_iotlb_memory to fail map single later if swiotlb is still needed. Suggested-by: Eric W. Biederman Signed-off-by: Yinghai Lu Cc: Konrad Rzeszutek Wilk Cc: Joerg Roedel --- arch/x86/kernel/pci-swiotlb.c | 2 - include/linux/swiotlb.h | 1 lib/swiotlb.c | 59 ++++++++++++++++++++++++++++++++++-------- 3 files changed, 50 insertions(+), 12 deletions(-) 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 +void swiotlb_init_nopanic(int verbose); 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 @@ -122,11 +122,18 @@ static dma_addr_t swiotlb_virt_to_bus(st return phys_to_dma(hwdev, virt_to_phys(address)); } +static bool no_iotlb_memory; + void swiotlb_print_info(void) { unsigned long bytes = io_tlb_nslabs << IO_TLB_SHIFT; unsigned char *vstart, *vend; + if (no_iotlb_memory) { + printk(KERN_INFO "software IO TLB: No low mem\n"); + return; + } + vstart = phys_to_virt(io_tlb_start); vend = phys_to_virt(io_tlb_end); @@ -136,7 +143,8 @@ void swiotlb_print_info(void) bytes >> 20, vstart, vend - 1); } -void __init swiotlb_init_with_tbl(char *tlb, unsigned long nslabs, int verbose) +static void __init __swiotlb_init_with_tbl(char *tlb, unsigned long nslabs, + int verbose, bool nopanic) { void *v_overflow_buffer; unsigned long i, bytes; @@ -150,9 +158,17 @@ void __init swiotlb_init_with_tbl(char * /* * Get the overflow emergency buffer */ - v_overflow_buffer = alloc_bootmem_low_pages(PAGE_ALIGN(io_tlb_overflow)); - if (!v_overflow_buffer) - panic("Cannot allocate SWIOTLB overflow buffer!\n"); + v_overflow_buffer = alloc_bootmem_low_pages_nopanic( + PAGE_ALIGN(io_tlb_overflow)); + if (!v_overflow_buffer) { + if (nopanic) { + no_iotlb_memory = true; + free_bootmem(io_tlb_start, + PAGE_ALIGN(io_tlb_nslabs << IO_TLB_SHIFT)); + return; + } else + panic("Cannot allocate SWIOTLB overflow buffer!\n"); + } io_tlb_overflow_buffer = __pa(v_overflow_buffer); @@ -171,12 +187,17 @@ void __init swiotlb_init_with_tbl(char * swiotlb_print_info(); } +void __init swiotlb_init_with_tbl(char *tlb, unsigned long nslabs, int verbose) +{ + __swiotlb_init_with_tbl(tlb, nslabs, verbose, false); +} + /* * Statically reserve bounce buffer space and initialize bounce buffer data * structures for the software IO TLB used to implement the DMA API. */ static void __init -swiotlb_init_with_default_size(size_t default_size, int verbose) +swiotlb_init_with_default_size(size_t default_size, int verbose, bool nopanic) { unsigned char *vstart; unsigned long bytes; @@ -191,17 +212,30 @@ swiotlb_init_with_default_size(size_t de /* * Get IO TLB memory from the low pages */ - vstart = alloc_bootmem_low_pages(PAGE_ALIGN(bytes)); - if (!vstart) - panic("Cannot allocate SWIOTLB buffer"); + vstart = alloc_bootmem_low_pages_nopanic(PAGE_ALIGN(bytes)); + if (!vstart) { + if (nopanic) { + no_iotlb_memory = true; + return; + } else + panic("Cannot allocate SWIOTLB buffer"); + } - swiotlb_init_with_tbl(vstart, io_tlb_nslabs, verbose); + __swiotlb_init_with_tbl(vstart, io_tlb_nslabs, verbose, nopanic); +} + +void __init +swiotlb_init_nopanic(int verbose) +{ + /* default to 64MB */ + swiotlb_init_with_default_size(64 * (1<<20), verbose, true); } void __init swiotlb_init(int verbose) { - swiotlb_init_with_default_size(64 * (1<<20), verbose); /* default to 64MB */ + /* default to 64MB */ + swiotlb_init_with_default_size(64 * (1<<20), verbose, false); } /* @@ -405,6 +439,9 @@ phys_addr_t swiotlb_tbl_map_single(struc unsigned long offset_slots; unsigned long max_slots; + if (no_iotlb_memory) + return SWIOTLB_MAP_ERROR; + mask = dma_get_seg_boundary(hwdev); tbl_dma_addr &= mask; @@ -669,7 +706,7 @@ swiotlb_full(struct device *dev, size_t printk(KERN_ERR "DMA: Out of SW-IOMMU space for %zu bytes at " "device %s\n", size, dev ? dev_name(dev) : "?"); - if (size <= io_tlb_overflow || !do_panic) + if (!no_iotlb_memory && (size <= io_tlb_overflow || !do_panic)) return; if (dir == DMA_BIDIRECTIONAL) 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 @@ -91,7 +91,7 @@ IOMMU_INIT(pci_swiotlb_detect_4gb, void __init pci_swiotlb_init(void) { if (swiotlb) { - swiotlb_init(0); + swiotlb_init_nopanic(0); dma_ops = &swiotlb_dma_ops; } }