linux-kernellinux-device-driverarm64dmapci-e

dma_set_mask_and_coherent() with 24-bit DMA mask for PCIe on arm64


I am writing a PCIe driver and have trouble with setting the DMA mask.

The host is a zcu102 with a Quadcore ARM-Cortex A53. The PCIe device is a custom Device.

The kernel is v5.15.0-1023-xilinx-zynqmp with the Xilinx Ubuntu 22.04.3 LTS.

According to DMA API HOWTO I need to set the DMA mask using dma_set_mask_and_coherent() My device supports a DMA mask of 24 bits. Therefore I try the following:

status=dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(24));

This returns -5 => -EIO => I/O error.

I have tried the following:

When I try the same code with DMA_BIT_MASK(32) it works. So it seems to be a problem with the 24 bits.

I looked at the kernel config gunzip -c /proc/config.gz | grep CONFIG_ZONE_DMA:

CONFIG_ZONE_DMA=y
CONFIG_ZONE_DMA32=y

Which looks okay to me.

My current problem trace is:

  1. dma_set_mask_and_coherent)_ calls dma_set_mask().

  2. dma_set_mask() checks if dma_supported()(dev,mask) returns zero

  3. For DMA_BIT_MASK(24) it returns 0 leading to failure. For DMA_BIT_MASK(32) it returns 1 leading to success.

  4. dma_supported() checks for a null pointer in get_dma_ops(dev)

  5. get_dma_ops() implementation depends on CONFIG_DMA_OPS

  6. gunzip -c /proc/config.gz | grep CONFIG_DMA_OPS returns CONFIG_DMA_OPS=y

  7. Since this is enabled: get_dma_ops() checks for dev->dma_ops

  8. pr_info("pdev->dev.dma_ops: %p\n", pdev->dev.dma_ops); returns 0 so the check fails

  9. That means dma_direct_supported() is called.

Could you please help me answer the following questions:

  • Why does dma_direct_supported fail?

Solution

  • With Ian Abbots Hint I took a look at dma_direct_supported() and found

    min_mask = min_t(u64, min_mask, DMA_BIT_MASK(zone_dma_bits));
    

    zone_dma_bits resolves to 32 for arm64 so the mask of 24 bits is too small.