diff --git a/include/condy/async_operations.hpp b/include/condy/async_operations.hpp index c1c5a763..c8aaa6e6 100644 --- a/include/condy/async_operations.hpp +++ b/include/condy/async_operations.hpp @@ -919,6 +919,17 @@ template inline auto async_ftruncate(Fd fd, loff_t len) { } #endif +#if !IO_URING_CHECK_VERSION(2, 8) // >= 2.8 +/** + * @brief See io_uring_prep_cmd_discard + */ +template +inline auto async_cmd_discard(Fd fd, uint64_t offset, uint64_t nbytes) { + auto op = make_op_awaiter(io_uring_prep_cmd_discard, fd, offset, nbytes); + return detail::maybe_flag_fixed_fd(std::move(op), fd); +} +#endif + #if !IO_URING_CHECK_VERSION(2, 7) // >= 2.7 /** * @brief See io_uring_prep_bind diff --git a/tests/test_async_operations.cpp b/tests/test_async_operations.cpp index f8560ec6..fc2a681e 100644 --- a/tests/test_async_operations.cpp +++ b/tests/test_async_operations.cpp @@ -3314,6 +3314,122 @@ TEST_CASE("test async_operations - test ftruncate - fixed fd") { } #endif +namespace { + +class BlkDevice { +public: + BlkDevice() { + char buf[] = "blkdevXXXXXX"; + int fd = mkstemp(buf); + if (fd < 0) { + return; + } + + file_path_ = buf; + + // truncate to 1MB + if (ftruncate(fd, 1024l * 1024l) != 0) { + close(fd); + file_path_.clear(); + return; + } + + close(fd); + + FILE *f = popen("losetup -f", "r"); + if (!f) { + return; + } + + char dev_path[256] = {0}; + if (fgets(dev_path, sizeof(dev_path), f) == nullptr) { + pclose(f); + return; + } + pclose(f); + + path_ = dev_path; + // remove trailing newline + if (!path_.empty() && path_.back() == '\n') { + path_.pop_back(); + } + + int r = system(("losetup " + path_ + " " + file_path_).c_str()); + if (r != 0) { + path_.clear(); + return; + } + } + + ~BlkDevice() { + if (!path_.empty()) { + // detach loop device + system(("losetup -d " + path_).c_str()); + } + if (!file_path_.empty()) { + unlink(file_path_.c_str()); + } + } + + BlkDevice(const BlkDevice &) = delete; + BlkDevice &operator=(const BlkDevice &) = delete; + BlkDevice(BlkDevice &&) = delete; + BlkDevice &operator=(BlkDevice &&) = delete; + + const std::string &path() const { return path_; } + +private: + std::string file_path_; + std::string path_; +}; + +} // namespace + +#if !IO_URING_CHECK_VERSION(2, 8) // >= 2.8 +TEST_CASE("test async_operations - test cmd_discard - basic") { + BlkDevice blkdev; + if (blkdev.path().empty()) { + MESSAGE("Can't create loop device, skipping"); + return; + } + + int fd = open(blkdev.path().c_str(), O_RDWR); + REQUIRE(fd >= 0); + + auto func = [&]() -> condy::Coro { + int r = co_await condy::async_cmd_discard(fd, 0, 4096); + REQUIRE(r == 0); + }; + condy::sync_wait(func()); + close(fd); +} +#endif + +#if !IO_URING_CHECK_VERSION(2, 8) // >= 2.8 +TEST_CASE("test async_operations - test cmd_discard - fixed fd") { + BlkDevice blkdev; + if (blkdev.path().empty()) { + MESSAGE("Can't create loop device, skipping"); + return; + } + + int fd = open(blkdev.path().c_str(), O_RDWR); + REQUIRE(fd >= 0); + + auto func = [&]() -> condy::Coro { + auto &fd_table = condy::current_runtime().fd_table(); + fd_table.init(1); + int r = co_await condy::async_files_update(&fd, 1, 0); + REQUIRE(r == 1); + + r = co_await condy::async_cmd_discard(condy::fixed(0), 0, 4096); + REQUIRE(r == 0); + }; + condy::sync_wait(func()); + close(fd); +} +#endif + #if !IO_URING_CHECK_VERSION(2, 7) // >= 2.7 TEST_CASE("test async_operations - test bind - basic") { int sock_fd = socket(AF_INET, SOCK_DGRAM, 0);