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_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 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)