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 onwait_flag(path): fd = os.open(path, os.O_RDWR | os.O_CREAT | os.O_NONBLOCK, 0o660) one = bytearray(6) two = bytearray(5) vec = liburing.iovec(one, two) ring = liburing.io_uring() cqes = liburing.io_uring_cqes() # print() try: # WRITE # ----- os.write(fd, b'hello world') os.fsync(fd) assert liburing.io_uring_queue_init(2, ring, 0) == 0 # READ # ---- sqe = liburing.io_uring_get_sqe(ring) liburing.io_uring_prep_readv(sqe, fd, vec, len(vec), 0, os.RWF_NOWAIT) sqe.user_data = 1 assert liburing.io_uring_submit(ring) == 1 while True: try: liburing.io_uring_peek_cqe(ring, cqes) except BlockingIOError: pass # print('test_rwf_nowait_flag BlockingIOError', flush=True) else: cqe = cqes[0] liburing.trap_error(cqe.res) assert cqe.res == 6 + 5 assert cqe.user_data == 1 assert one == b'hello ' assert two == b'world' liburing.io_uring_cqe_seen(ring, cqe) break finally: liburing.io_uring_queue_exit(ring) os.close(fd) os.unlink(path)
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 onwait_flag_empty_file(path, flag): fd = os.open(path, os.O_RDWR | os.O_CREAT | os.O_NONBLOCK, 0o660) read = bytearray(5) vec = liburing.iovec(read) ring = liburing.io_uring() cqes = liburing.io_uring_cqes() try: assert liburing.io_uring_queue_init(2, ring, 0) == 0 # READ empty file # --------------- sqe = liburing.io_uring_get_sqe(ring) liburing.io_uring_prep_readv(sqe, fd, vec, len(vec), 0, flag) sqe.user_data = 1 assert liburing.io_uring_submit(ring) == 1 while True: try: liburing.io_uring_peek_cqe(ring, cqes) except BlockingIOError: pass # print('test_rwf_nowait_flag BlockingIOError', flush=True) else: cqe = cqes[0] # empty file with `RWF_NOWAIT` flag will return `-EAGAIN` rather then `0` if flag & os.RWF_NOWAIT: assert cqe.res == -errno.EAGAIN else: assert cqe.res == 0 liburing.io_uring_cqe_seen(ring, cqe) break finally: liburing.io_uring_queue_exit(ring) os.close(fd) os.unlink(path)
def test_event(tmpdir): fd = os.open(os.path.join(tmpdir, 'event.txt'), os.O_RDWR | os.O_CREAT, 0o660) ring = liburing.io_uring() cqes = liburing.io_uring_cqes() # prepare for writing. data = bytearray(b'hello world') vecs = liburing.iovec(data) try: # initialization assert liburing.io_uring_queue_init(32, ring, 0) == 0 sqe = liburing.io_uring_get_sqe(ring) liburing.io_uring_prep_writev(sqe, fd, vecs, len(vecs), 0) assert liburing.io_uring_submit(ring) == 1 while True: try: liburing.io_uring_peek_cqe(ring, cqes) except BlockingIOError: pass # waiting for events, do something else here. else: cqe = cqes[0] assert cqe.res == 11 liburing.io_uring_cqe_seen(ring, cqe) break # confirm content = os.read(fd, 100) assert content == data assert len(content) == vecs[0].iov_len finally: os.close(fd) liburing.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 test_files_write_read(tmpdir): fd = os.open(os.path.join(tmpdir, '1.txt'), os.O_RDWR | os.O_CREAT, 0o660) ring = liburing.io_uring() cqes = liburing.io_uring_cqes() # prepare for writing two separate writes and reads. one = bytearray(b'hello') two = bytearray(b'world') vec_one = liburing.iovec(one) vec_two = liburing.iovec(two) try: # initialization assert liburing.io_uring_queue_init(2, ring, 0) == 0 # write "hello" sqe = liburing.io_uring_get_sqe(ring) # get sqe (submission queue entry) to fill liburing.io_uring_prep_write(sqe, fd, vec_one[0].iov_base, vec_one[0].iov_len, 0) sqe.user_data = 1 # write "world" sqe = liburing.io_uring_get_sqe(ring) liburing.io_uring_prep_writev(sqe, fd, vec_two, len(vec_two), 5) sqe.user_data = 2 # submit both writes assert liburing.io_uring_submit(ring) == 2 # wait for ``2`` entry to complete using single syscall assert liburing.io_uring_wait_cqes(ring, cqes, 2) == 0 cqe = cqes[0] assert cqe.res == 5 assert cqe.user_data == 1 liburing.io_uring_cqe_seen(ring, cqe) # re-uses the same resources from above?! assert liburing.io_uring_wait_cqes(ring, cqes, 1) == 0 cqe = cqes[0] assert cqe.res == 5 assert cqe.user_data == 2 liburing.io_uring_cqe_seen(ring, cqe) # Using same ``vec*`` swap so read can be confirmed. # read "world" sqe = liburing.io_uring_get_sqe(ring) liburing.io_uring_prep_readv(sqe, fd, vec_one, len(vec_one), 5) sqe.user_data = 3 assert liburing.io_uring_submit(ring) == 1 assert liburing.io_uring_wait_cqe(ring, cqes) == 0 cqe = cqes[0] assert cqe.res == 5 assert cqe.user_data == 3 liburing.io_uring_cq_advance(ring, 1) # read "hello" sqe = liburing.io_uring_get_sqe(ring) liburing.io_uring_prep_read(sqe, fd, vec_two[0].iov_base, vec_two[0].iov_len, 0) sqe.user_data = 4 assert liburing.io_uring_submit(ring) == 1 assert liburing.io_uring_wait_cqe(ring, cqes) == 0 cqe = cqes[0] assert cqe.res == 5 assert cqe.user_data == 4 liburing.io_uring_cq_advance(ring, 1) # use same as write buffer to read but switch values so the change is detected assert one == b'world' assert two == b'hello' finally: os.close(fd) liburing.io_uring_queue_exit(ring)
def submit_write_entry(self, fd, data, offset): sqe = self._get_sqe() iovecs = liburing.iovec(data) liburing.io_uring_prep_writev(sqe, fd, iovecs, len(iovecs), offset) return self._submit(RequestType.WRITE, sqe)
def submit_read_entry(self, fd, size, offset): sqe = self._get_sqe() array = bytearray(size) iovecs = liburing.iovec(array) liburing.io_uring_prep_readv(sqe, fd, iovecs, len(iovecs), offset) return self._submit(RequestType.READ, sqe, array)