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的倍数。
其他一些注意事项:
- 即使我从标志中删除 O_SYNC 到 open 函数,我也会得到同样的错误。
- 进入 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
文件操作处理程序。
您 mmap
ed 的物理内存被映射为页表中的非缓存内存,因此不需要刷新写入。但是,您可能应该使用 volatile
访问内存映射的 I/O 寄存器。内核中的readb
和writeb
等寄存器访问函数是特定于体系结构的,但总是将地址转换为指向volatile
整数类型的指针(例如volatile unsigned char *
, volatile unsigned short *
或 volatile unsigned int *
取决于寄存器访问的宽度)在访问内存位置之前。
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的倍数。
其他一些注意事项:
- 即使我从标志中删除 O_SYNC 到 open 函数,我也会得到同样的错误。
- 进入 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
文件操作处理程序。
您 mmap
ed 的物理内存被映射为页表中的非缓存内存,因此不需要刷新写入。但是,您可能应该使用 volatile
访问内存映射的 I/O 寄存器。内核中的readb
和writeb
等寄存器访问函数是特定于体系结构的,但总是将地址转换为指向volatile
整数类型的指针(例如volatile unsigned char *
, volatile unsigned short *
或 volatile unsigned int *
取决于寄存器访问的宽度)在访问内存位置之前。