UIO 和 msync:为什么 msync return "invalid argument" 即使地址是 PAGESIZE 的倍数

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

Linux版本:4.19
平台:Xilinx Ultrascale+ Zynq

在可编程逻辑中,我创建了一个位于物理地址 0xA0001000 的内存映射设备。我使用 uio_pdrv_genirq 作为我的设备驱动程序。该设备显示为 uio0,我已准备好使用 mmap 读取和写入它。我希望能够保证我所做的任何写入都立即写入设备,而不是等待 Linux 自行刷新脏页。为此,我应该根据我的所有研究使用 msync。但是当我这样做时,我总是会出错。这是我的测试程序:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/mman.h>
#include <unistd.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <unistd.h>

void main() {
    int fid;
    int rval;
    char *data;
    int idx;

    printf("Open UIO Device \n");
    fid= open("/dev/uio0", O_RDWR | O_SYNC);

    data= mmap(NULL, 0x1000, PROT_WRITE|PROT_READ, MAP_SHARED, fid, 0);
    if(MAP_FAILED == data) {
        printf("Error code when mapping! %s", strerror(errno));
    }
    printf("addr= 0x%8X\n", data);
    printf("pagesize= 0x%4X\n", getpagesize());

    printf("Write some data\n");
    data[11]= 0xDE;
    data[10]= 0xC0;
    data[ 9]= 0xDE;
    data[ 8]= 0xAD;

    rval= msync(data, 0x1000, MS_SYNC);
    if(-1 == rval) {
        printf("Error on msync! %s\n", strerror(errno));
    }

    if(munmap(data, 0x1000) < 0) {
        printf("munmap error! %s\n", strerror(errno));
    }

    printf("Close UIO device\n");
    rval= close(fid);
    if(rval != 0) {
        printf("UIO device close Error!\n");
    }
}

这是程序输出:

mylinux:~/test-apps$ ./a.out
Open UIO Device
addr= 0xABA05000
pagesize= 0x1000
Write some data
Error on msync! Invalid argument
Close UIO device

我不明白这个错误的来源。 msync 手册页指出,如果地址不是 PAGESIZE 的倍数,就会发生此错误。但是从上面的例子可以看出,它是一个倍数。连物理地址都是PAGESIZE的倍数。

其他一些注意事项:

  1. 即使我从标志中删除 O_SYNC 到 open 函数,我也会得到同样的错误。
  2. 进入 FPGA 架构的 AXI4 总线是 128 位,然后由 Xilinx IP 块转换为 32 位。我不认为这与我的问题有任何关系。

感谢大家提供的任何见解。

我认为 EINVAL 错误是因为 msync 系统调用(在“mm/msync.c”中)的内核处理程序调用 vfs_fsync_range:

            error = vfs_fsync_range(file, fstart, fend, 1);

vfs_fsync_range(在“fs/sync.c”中)在此处返回 -EINVAL

    if (!file->f_op->fsync)
        return -EINVAL;

因为UIO驱动核心(在“drivers/uio/uio.c”中没有设置fsync文件操作处理程序。


mmaped 的物理内存被映射为页表中的非缓存内存,因此不需要刷新写入。但是,您可能应该使用 volatile 访问内存映射的 I/O 寄存器。内核中的readbwriteb等寄存器访问函数是特定于体系结构的,但总是将地址转换为指向volatile整数类型的指针(例如volatile unsigned char *volatile unsigned short *volatile unsigned int * 取决于寄存器访问的宽度)在访问内存位置之前。