def test_openat2_resolve_cache(tmpdir): ring = io_uring() cqes = io_uring_cqes() try: assert io_uring_queue_init(2, ring, 0) == 0 how = open_how(O_CREAT, 0o600, RESOLVE_CACHED) lookup_path = join(tmpdir, 'lookup-file.txt').encode() for _ in range(2, 0, -1): # countdown try: sqe = io_uring_get_sqe(ring) io_uring_prep_openat2(sqe, AT_FDCWD, lookup_path, how) fd = submit_wait_result(ring, cqes) except BlockingIOError: if how[0].resolve & RESOLVE_CACHED: how[0].resolve = how[0].resolve & ~RESOLVE_CACHED # note: # must retry without `RESOLVE_CACHED` since # file path was not in kernel's lookup cache. else: assert fd > 0 # close `fd` sqe = io_uring_get_sqe(ring) io_uring_prep_close(sqe, fd) assert submit_wait_result(ring, cqes) == 0 break else: assert False # failed to create file finally: io_uring_queue_exit(ring)
def test_openat2(tmpdir): ring = io_uring() cqes = io_uring_cqes() # prep buffers = (bytearray(b'hello world'), bytearray(11)) iov = iovec(*buffers) file_path = join(tmpdir, 'openat2_test.txt').encode() try: assert io_uring_queue_init(2, ring, 0) == 0 assert io_uring_register_buffers(ring, iov, len(iov)) == 0 # open how = open_how(O_CREAT | O_RDWR, 0o700) sqe = io_uring_get_sqe(ring) io_uring_prep_openat2(sqe, AT_FDCWD, file_path, how) fd = submit_wait_result(ring, cqes) # write sqe = io_uring_get_sqe(ring) io_uring_prep_write(sqe, fd, iov[0].iov_base, iov[0].iov_len, 0) assert submit_wait_result(ring, cqes) == 11 # read sqe = io_uring_get_sqe(ring) io_uring_prep_read(sqe, fd, iov[1].iov_base, iov[1].iov_len, 0) assert submit_wait_result(ring, cqes) == 11 # confirm assert buffers[0] == buffers[1] # close sqe = io_uring_get_sqe(ring) io_uring_prep_close(sqe, fd) submit_wait_result(ring, cqes) if skip_os('5.12'): skip('RESOLVE_CACHED 5.12+ Linux required') else: # second open for resolve test how = open_how(O_RDWR, 0, RESOLVE_CACHED) sqe = io_uring_get_sqe(ring) io_uring_prep_openat2(sqe, AT_FDCWD, file_path, how) fd2 = submit_wait_result(ring, cqes) assert fd2 > 0 # close `fd2` sqe = io_uring_get_sqe(ring) io_uring_prep_close(sqe, fd2) assert submit_wait_result(ring, cqes) == 0 finally: io_uring_queue_exit(ring)
def test_file_o_direct(): # note: # - `O_DIRECT` does not work if the file path is in memory, like "/dev/shm" or "/tmp" # - currently only `MAP_PRIVATE` works with `io_uring_register_buffers`, `MAP_SHARED` will be fixed soon! ring = io_uring() cqes = io_uring_cqes() if skip_os('5.14'): # was a bug < 5.14 read = mmap(-1, PAGESIZE, flags=MAP_PRIVATE) write = mmap(-1, PAGESIZE, flags=MAP_PRIVATE) else: read = mmap(-1, PAGESIZE) # MAP_SHARED write = mmap(-1, PAGESIZE) write[0:11] = b'hello world' iov = iovec(write, read) try: assert io_uring_queue_init(2, ring, 0) == 0 assert io_uring_register_buffers(ring, iov, len(iov)) == 0 # open - create local testing file. sqe = io_uring_get_sqe(ring) io_uring_prep_openat(sqe, AT_FDCWD, b'.', O_TMPFILE | O_RDWR | O_DIRECT, 0o700) fd = submit_wait_result(ring, cqes) # write sqe = io_uring_get_sqe(ring) io_uring_prep_write_fixed(sqe, fd, iov[0].iov_base, iov[0].iov_len, 0, 0) assert submit_wait_result(ring, cqes) == PAGESIZE # read sqe = io_uring_get_sqe(ring) io_uring_prep_read_fixed(sqe, fd, iov[1].iov_base, iov[1].iov_len, 0, 1) assert submit_wait_result(ring, cqes) == PAGESIZE # confirm assert read[:20] == write[:20] # close sqe = io_uring_get_sqe(ring) io_uring_prep_close(sqe, fd) assert submit_wait_result(ring, cqes) == 0 finally: read.close() write.close() io_uring_queue_exit(ring)
def test_register_fd_close(): ring = io_uring() cqes = io_uring_cqes() write = bytearray(b'hello world') read = bytearray(11) iov = iovec(write, read) try: assert io_uring_queue_init(2, ring, 0) == 0 # open - create local testing file. sqe = io_uring_get_sqe(ring) io_uring_prep_openat(sqe, AT_FDCWD, b'.', O_TMPFILE | O_RDWR, 0o700) fd = submit_wait_result(ring, cqes) # register `fds` fds = files(fd) # `int[fd]` index = 0 assert io_uring_register_files(ring, fds, len(fds)) == 0 # close - right away after registering fd sqe = io_uring_get_sqe(ring) io_uring_prep_close(sqe, fd) assert submit_wait_result(ring, cqes) == 0 # write - using registered file index and closed fd sqe = io_uring_get_sqe(ring) io_uring_prep_write(sqe, index, iov[0].iov_base, iov[0].iov_len, 0) sqe.flags |= IOSQE_FIXED_FILE assert submit_wait_result(ring, cqes) == 11 # read - using registered file index and closed fd sqe = io_uring_get_sqe(ring) io_uring_prep_read(sqe, index, iov[1].iov_base, iov[1].iov_len, 0) sqe.flags |= IOSQE_FIXED_FILE assert submit_wait_result(ring, cqes) == 11 # confirm assert write == read # unregister - index fds = files(-1) assert io_uring_register_files_update(ring, index, fds, 1) == 1 # re-read - should not be able to since index was unregistered with raises(OSError): # [Errno 9] Bad file descriptor sqe = io_uring_get_sqe(ring) io_uring_prep_read(sqe, index, iov[1].iov_base, iov[1].iov_len, 0) sqe.flags |= IOSQE_FIXED_FILE assert submit_wait_result(ring, cqes) == 11 finally: io_uring_queue_exit(ring)
def test_register_file_update(): ring = io_uring() cqes = io_uring_cqes() write = bytearray(b'hello world') read = bytearray(11) iov = iovec(write, read) fds = files(-1, -1, -1, -1) # prep 4 fd spots index = 2 # only use index ``2`` of the `fds` try: assert io_uring_queue_init(2, ring, 0) == 0 # register 4 `fds` assert io_uring_register_files(ring, fds, len(fds)) == 0 # open - create local testing file. sqe = io_uring_get_sqe(ring) io_uring_prep_openat(sqe, AT_FDCWD, b'.', O_TMPFILE | O_RDWR, 0o700) fd = submit_wait_result(ring, cqes) fds[index] = fd fd2 = files(fd) # `int[fd]` # register - update only the index ``2`` value assert io_uring_register_files_update(ring, index, fd2, 1) == 1 # close sqe = io_uring_get_sqe(ring) io_uring_prep_close(sqe, fds[index]) assert submit_wait_result(ring, cqes) == 0 # write - using registered file index vs fd sqe = io_uring_get_sqe(ring) io_uring_prep_write(sqe, index, iov[0].iov_base, iov[0].iov_len, 0) sqe.flags |= IOSQE_FIXED_FILE assert submit_wait_result(ring, cqes) == 11 # read - using registered file index vs fd sqe = io_uring_get_sqe(ring) io_uring_prep_read(sqe, index, iov[1].iov_base, iov[1].iov_len, 0) sqe.flags |= IOSQE_FIXED_FILE assert submit_wait_result(ring, cqes) == 11 # confirm assert write == read # unregister assert io_uring_unregister_files(ring) == 0 finally: io_uring_queue_exit(ring)
def test_register_file(): ring = io_uring() cqes = io_uring_cqes() write = bytearray(b'hello world') read = bytearray(11) iov = iovec(write, read) try: assert io_uring_queue_init(2, ring, 0) == 0 # open - create local testing file. sqe = io_uring_get_sqe(ring) io_uring_prep_openat(sqe, AT_FDCWD, b'.', O_TMPFILE | O_RDWR, 0o700) fd = submit_wait_result(ring, cqes) # register `fds` fds = files(fd) # `int[fd]` index = 0 assert io_uring_register_files(ring, fds, len(fds)) == 0 # close sqe = io_uring_get_sqe(ring) io_uring_prep_close(sqe, fd) assert submit_wait_result(ring, cqes) == 0 # write - using registered file index vs fd sqe = io_uring_get_sqe(ring) io_uring_prep_write(sqe, index, iov[0].iov_base, iov[0].iov_len, 0) sqe.flags |= IOSQE_FIXED_FILE assert submit_wait_result(ring, cqes) == 11 # read - using registered file index vs fd sqe = io_uring_get_sqe(ring) io_uring_prep_read(sqe, index, iov[1].iov_base, iov[1].iov_len, 0) sqe.flags |= IOSQE_FIXED_FILE assert submit_wait_result(ring, cqes) == 11 # confirm assert write == read finally: # note: let exit also unregister fds io_uring_queue_exit(ring)
def test_multiple_register_fd_mix(tmpdir): ring = io_uring() cqes = io_uring_cqes() loop = randint(2, 5) data = b'hello world' write = [bytearray(data) for _ in range(loop)] iov = iovec(*write) fds = files(-1 for _ in range(loop)) # prep fds try: assert io_uring_queue_init(2, ring, 0) == 0 # initialize register assert io_uring_register_files(ring, fds, len(fds)) == 0 for index in range(loop): # index = 0, 1, ... path = join(tmpdir, f'register-fd-mix-{index}.txt').encode() # open sqe = io_uring_get_sqe(ring) io_uring_prep_openat(sqe, AT_FDCWD, path, O_CREAT | O_RDWR, 0o700) fd = submit_wait_result(ring, cqes) # register update if index == 1: # skip registering middle one! fd1 = fd else: fds = files(fd) assert io_uring_register_files_update(ring, index, fds, 1) == 1 # close registered fd only sqe = io_uring_get_sqe(ring) io_uring_prep_close(sqe, fd) assert submit_wait_result(ring, cqes) == 0 # write for index in range(loop): sqe = io_uring_get_sqe(ring) if index == 1: # write using fd io_uring_prep_write(sqe, fd1, iov[index].iov_base, iov[index].iov_len, 0) else: # write using registered file index io_uring_prep_write(sqe, index, iov[index].iov_base, iov[index].iov_len, 0) sqe.flags |= IOSQE_FIXED_FILE assert submit_wait_result(ring, cqes) == 11 # read for index in range(loop): path = join(tmpdir, f'register-fd-mix-{index}.txt').encode() stat = statx() sqe = io_uring_get_sqe(ring) io_uring_prep_statx(sqe, AT_FDCWD, path, 0, 0, stat) assert submit_wait_result(ring, cqes) == 0 length = stat[0].stx_size # get written file size # note: even though size is known, get it using statx read = bytearray(length) buffer = from_buffer(read) sqe = io_uring_get_sqe(ring) if index == 1: io_uring_prep_read(sqe, fd1, buffer, length, 0) else: # read using registered file index io_uring_prep_read(sqe, index, buffer, length, 0) sqe.flags |= IOSQE_FIXED_FILE assert submit_wait_result(ring, cqes) == 11 assert read == data # close for index in range(loop): if index == 1: sqe = io_uring_get_sqe(ring) io_uring_prep_close(sqe, fd1) assert submit_wait_result(ring, cqes) == 0 else: # unregister file index fds = files(-1) assert io_uring_register_files_update(ring, index, fds, 1) == 1 finally: io_uring_queue_exit(ring)
def close_file(ring, cqes, fd): sqe = io_uring_get_sqe(ring) io_uring_prep_close(sqe, fd) assert submit_wait_result(ring, cqes) == 0
def submit_close_entry(self, fd): sqe = self._get_sqe() liburing.io_uring_prep_close(sqe, fd) return self._submit(RequestType.CLOSE, sqe)