Skip to content

Commit a409d96

Browse files
justin-heChristoph Hellwig
authored andcommitted
dma-mapping: fix dma_addressing_limited() if dma_range_map can't cover all system RAM
There is an unusual case that the range map covers right up to the top of system RAM, but leaves a hole somewhere lower down. Then it prevents the nvme device dma mapping in the checking path of phys_to_dma() and causes the hangs at boot. E.g. On an Armv8 Ampere server, the dsdt ACPI table is: Method (_DMA, 0, Serialized) // _DMA: Direct Memory Access { Name (RBUF, ResourceTemplate () { QWordMemory (ResourceConsumer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, 0x0000000000000000, // Granularity 0x0000000000000000, // Range Minimum 0x00000000FFFFFFFF, // Range Maximum 0x0000000000000000, // Translation Offset 0x0000000100000000, // Length ,, , AddressRangeMemory, TypeStatic) QWordMemory (ResourceConsumer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, 0x0000000000000000, // Granularity 0x0000006010200000, // Range Minimum 0x000000602FFFFFFF, // Range Maximum 0x0000000000000000, // Translation Offset 0x000000001FE00000, // Length ,, , AddressRangeMemory, TypeStatic) QWordMemory (ResourceConsumer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, 0x0000000000000000, // Granularity 0x00000060F0000000, // Range Minimum 0x00000060FFFFFFFF, // Range Maximum 0x0000000000000000, // Translation Offset 0x0000000010000000, // Length ,, , AddressRangeMemory, TypeStatic) QWordMemory (ResourceConsumer, PosDecode, MinFixed, MaxFixed, Cacheable, ReadWrite, 0x0000000000000000, // Granularity 0x0000007000000000, // Range Minimum 0x000003FFFFFFFFFF, // Range Maximum 0x0000000000000000, // Translation Offset 0x0000039000000000, // Length ,, , AddressRangeMemory, TypeStatic) }) But the System RAM ranges are: cat /proc/iomem |grep -i ram 90000000-91ffffff : System RAM 92900000-fffbffff : System RAM 880000000-fffffffff : System RAM 8800000000-bff5990fff : System RAM bff59d0000-bff5a4ffff : System RAM bff8000000-bfffffffff : System RAM So some RAM ranges are out of dma_range_map. Fix it by checking whether each of the system RAM resources can be properly encompassed within the dma_range_map. Signed-off-by: Jia He <justin.he@arm.com> Signed-off-by: Christoph Hellwig <hch@lst.de>
1 parent 8ae0e97 commit a409d96

File tree

3 files changed

+50
-2
lines changed

3 files changed

+50
-2
lines changed

kernel/dma/direct.c

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -587,6 +587,46 @@ int dma_direct_supported(struct device *dev, u64 mask)
587587
return mask >= phys_to_dma_unencrypted(dev, min_mask);
588588
}
589589

590+
/*
591+
* To check whether all ram resource ranges are covered by dma range map
592+
* Returns 0 when further check is needed
593+
* Returns 1 if there is some RAM range can't be covered by dma_range_map
594+
*/
595+
static int check_ram_in_range_map(unsigned long start_pfn,
596+
unsigned long nr_pages, void *data)
597+
{
598+
unsigned long end_pfn = start_pfn + nr_pages;
599+
const struct bus_dma_region *bdr = NULL;
600+
const struct bus_dma_region *m;
601+
struct device *dev = data;
602+
603+
while (start_pfn < end_pfn) {
604+
for (m = dev->dma_range_map; PFN_DOWN(m->size); m++) {
605+
unsigned long cpu_start_pfn = PFN_DOWN(m->cpu_start);
606+
607+
if (start_pfn >= cpu_start_pfn &&
608+
start_pfn - cpu_start_pfn < PFN_DOWN(m->size)) {
609+
bdr = m;
610+
break;
611+
}
612+
}
613+
if (!bdr)
614+
return 1;
615+
616+
start_pfn = PFN_DOWN(bdr->cpu_start) + PFN_DOWN(bdr->size);
617+
}
618+
619+
return 0;
620+
}
621+
622+
bool dma_direct_all_ram_mapped(struct device *dev)
623+
{
624+
if (!dev->dma_range_map)
625+
return true;
626+
return !walk_system_ram_range(0, PFN_DOWN(ULONG_MAX) + 1, dev,
627+
check_ram_in_range_map);
628+
}
629+
590630
size_t dma_direct_max_mapping_size(struct device *dev)
591631
{
592632
/* If SWIOTLB is active, use its maximum mapping size */

kernel/dma/direct.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ int dma_direct_mmap(struct device *dev, struct vm_area_struct *vma,
2020
bool dma_direct_need_sync(struct device *dev, dma_addr_t dma_addr);
2121
int dma_direct_map_sg(struct device *dev, struct scatterlist *sgl, int nents,
2222
enum dma_data_direction dir, unsigned long attrs);
23+
bool dma_direct_all_ram_mapped(struct device *dev);
2324
size_t dma_direct_max_mapping_size(struct device *dev);
2425

2526
#if defined(CONFIG_ARCH_HAS_SYNC_DMA_FOR_DEVICE) || \

kernel/dma/mapping.c

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -803,8 +803,15 @@ EXPORT_SYMBOL(dma_set_coherent_mask);
803803
*/
804804
bool dma_addressing_limited(struct device *dev)
805805
{
806-
return min_not_zero(dma_get_mask(dev), dev->bus_dma_limit) <
807-
dma_get_required_mask(dev);
806+
const struct dma_map_ops *ops = get_dma_ops(dev);
807+
808+
if (min_not_zero(dma_get_mask(dev), dev->bus_dma_limit) <
809+
dma_get_required_mask(dev))
810+
return true;
811+
812+
if (unlikely(ops))
813+
return false;
814+
return !dma_direct_all_ram_mapped(dev);
808815
}
809816
EXPORT_SYMBOL_GPL(dma_addressing_limited);
810817

0 commit comments

Comments
 (0)