Skip to content
Advertisement

UIO and msync: Why does msync return “invalid argument” even though the address is a multiple of PAGESIZE

Linux version: 4.19
Platform: Xilinx Ultrascale+ Zynq

In the programmable logic I’ve created a memory mapped device located at physical address 0xA0001000. I’m using uio_pdrv_genirq as my device driver. The device shows up as uio0 and I’m ready to read and write to it, using mmap. I want to be able to guarantee that any writes that I make get written to the device right away instead of waiting for Linux to flush the dirty pages on its own. For that I should use msync according to all my research. But I keep getting an error when I do that. Here’s my test program:

JavaScript

And here’s the program output:

JavaScript

I’m not understanding the source of this error. The msync man pages states that this error can occur if the address is not a multiple of PAGESIZE. But as you can see from the example above, it is a multiple. Even the physical address is a multiple of the PAGESIZE.

A few other notes:

  1. I get the same error even I remove O_SYNC from the flags to the open function.
  2. The AXI4 bus into the FPGA fabric is 128 bits, which is then converted to 32 bits by Xilinx IP blocks. I don’t think this would have anything to do with my problem.

Thanks for any insights folks can provide.

Advertisement

Answer

I believe the EINVAL error is because the kernel’s handler for the msync syscall (in “mm/msync.c“) calls vfs_fsync_range:

JavaScript

and vfs_fsync_range (in “fs/sync.c“) is returning -EINVAL here:

JavaScript

because the UIO driver core (in “drivers/uio/uio.c” does not set the fsync file operation handler.


The physical memory you have mmaped is mapped as non-cached memory in the page tables, so writes do not need to be flushed. However, you should probably use volatile accesses to the memory mapped I/O registers. The register access functions such as readb and writeb in the kernel are architecture-specific, but invariably convert the address to a pointer to a volatile integer type (e.g. volatile unsigned char *, volatile unsigned short *, or volatile unsigned int * depending on the width of the register access) before accessing the memory location.

User contributions licensed under: CC BY-SA
10 People found this is helpful
Advertisement