Everybody: I really appreciate the enthusiasm to get to the bottom of this mystery !
For the record, my device is unseekable.
Here come more insights using strace
as suggested by Barry:
Fail case with open
:
## the script
$ cat open.py
import mmap
fd = open('/dev/xdma1_user', 'wb')
mm = mmap.mmap(fd.fileno(), 4096, flags=mmap.MAP_SHARED, prot=mmap.PROT_READ|mmap.PROT_WRITE)
print('0:4', mm[0:4])
## run the script
$ strace -e abbrev=none -o open.txt python3 open.py
Traceback (most recent call last):
File "/home/dev/open.py", line 4, in <module>
mm = mmap.mmap(fd.fileno(), 4096, flags=mmap.MAP_SHARED, prot=mmap.PROT_READ|mmap.PROT_WRITE)
PermissionError: [Errno 13] Permission denied
## locating mmap for the fd open.txt
openat(AT_FDCWD, "/dev/xdma1_user", O_WRONLY|O_CREAT|O_TRUNC|O_CLOEXEC, 0666) = 3
newfstatat(3, "", {st_dev=makedev(0, 0x5), st_ino=807, st_mode=S_IFCHR|0666, st_nlink=1, st_uid=0, st_gid=0, st_blksize=4096, st_blocks=0, st_rdev=makedev(0x1ff, 0), st_atime=1714133103 /* 2024-04-26T14:05:03.330908530+0200 */, st_atime_nsec=330908530, st_mtime=1714133103 /* 2024-04-26T14:05:03.330908530+0200 */, st_mtime_nsec=330908530, st_ctime=1714133103 /* 2024-04-26T14:05:03.330908530+0200 */, st_ctime_nsec=330908530}, AT_EMPTY_PATH) = 0
ioctl(3, TCGETS, 0x7fff187eb400) = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR) = -1 ESPIPE (Illegal seek)
newfstatat(3, "", {st_dev=makedev(0, 0x5), st_ino=807, st_mode=S_IFCHR|0666, st_nlink=1, st_uid=0, st_gid=0, st_blksize=4096, st_blocks=0, st_rdev=makedev(0x1ff, 0), st_atime=1714133103 /* 2024-04-26T14:05:03.330908530+0200 */, st_atime_nsec=330908530, st_mtime=1714133103 /* 2024-04-26T14:05:03.330908530+0200 */, st_mtime_nsec=330908530, st_ctime=1714133103 /* 2024-04-26T14:05:03.330908530+0200 */, st_ctime_nsec=330908530}, AT_EMPTY_PATH) = 0
fcntl(3, F_DUPFD_CLOEXEC, 0) = 4
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = -1 EACCES (Permission denied)
close(4) = 0
Success case with os.open
:
## the script
$ cat osopen.py
import mmap
import os
fd = os.open('/dev/xdma1_user', os.O_RDWR)
mm = mmap.mmap(fd, 4096, flags=mmap.MAP_SHARED, prot=mmap.PROT_READ|mmap.PROT_WRITE)
print('0:4', mm[0:4])
## run the script
$ strace -e abbrev=none -o osopen.txt python3 osopen.py
0:4 b'\x01\x01\x00\x00'
## locating mmap for the fd in osopen.txt
openat(AT_FDCWD, "/dev/xdma1_user", O_RDWR|O_CLOEXEC) = 3
newfstatat(3, "", {st_dev=makedev(0, 0x5), st_ino=807, st_mode=S_IFCHR|0666, st_nlink=1, st_uid=0, st_gid=0, st_blksize=4096, st_blocks=0, st_rdev=makedev(0x1ff, 0), st_atime=1714133103 /* 2024-04-26T14:05:03.330908530+0200 */, st_atime_nsec=330908530, st_mtime=1714133103 /* 2024-04-26T14:05:03.330908530+0200 */, st_mtime_nsec=330908530, st_ctime=1714133103 /* 2024-04-26T14:05:03.330908530+0200 */, st_ctime_nsec=330908530}, AT_EMPTY_PATH) = 0
fcntl(3, F_DUPFD_CLOEXEC, 0) = 4
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED, 3, 0) = 0x7fd8a9f04000
write(1, "0:4 b'\\x01\\x01\\x00\\x00'\n", 24) = 24
The difference I can spot between the two cases is a) arguments for the openat()
and b) the attempt to seek in case of open
while os.open
does not try to seek.
FWIW, the error PermissionError: [Errno 13] Permission denied
would IMHO correspond to EACCES
and not to EPERM
. The Linux mmap(2) man page is quite loose in explaining what would result in EACCES
, though.
And finally, this was done with Python 3.10.12
.