mirror of
https://github.com/matrix-construct/construct
synced 2024-06-10 05:58:56 +02:00
ircd::fs: Add asio glue for io_uring read/write. (closes #37)
This commit is contained in:
parent
da55b185da
commit
ae9f2c1199
|
@ -53,11 +53,26 @@ namespace boost
|
||||||
#include <boost/system/system_error.hpp>
|
#include <boost/system/system_error.hpp>
|
||||||
#include <boost/date_time/posix_time/ptime.hpp>
|
#include <boost/date_time/posix_time/ptime.hpp>
|
||||||
|
|
||||||
|
// In boost 1.78+ io_uring(7) becomes the core event loop replacing epoll(7)
|
||||||
|
// and used for network operations.
|
||||||
#if defined(HAVE_LIBURING_H) \
|
#if defined(HAVE_LIBURING_H) \
|
||||||
&& IRCD_USE_URING == 1 \
|
&& IRCD_USE_URING == 1 \
|
||||||
&& BOOST_VERSION >= 107800
|
&& BOOST_VERSION >= 107800
|
||||||
#define BOOST_ASIO_HAS_IO_URING
|
#define BOOST_ASIO_HAS_IO_URING
|
||||||
#define BOOST_ASIO_DISABLE_EPOLL
|
#define BOOST_ASIO_DISABLE_EPOLL
|
||||||
|
#define IRCD_USE_ASIO_IO_URING 1
|
||||||
|
#else
|
||||||
|
#define IRCD_USE_ASIO_IO_URING 0
|
||||||
|
#endif
|
||||||
|
|
||||||
|
// In boost 1.79+ asio implements some filesystem operations we can use. While
|
||||||
|
// these are available in 1.78 they were buggy for our purposes until 1.79.
|
||||||
|
#if BOOST_VERSION >= 107900
|
||||||
|
#define IRCD_USE_ASIO_READ 1
|
||||||
|
#define IRCD_USE_ASIO_WRITE 1
|
||||||
|
#else
|
||||||
|
#define IRCD_USE_ASIO_READ 0
|
||||||
|
#define IRCD_USE_ASIO_WRITE 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include <boost/asio/detail/config.hpp>
|
#include <boost/asio/detail/config.hpp>
|
||||||
|
|
|
@ -31,4 +31,7 @@ namespace ircd::fs
|
||||||
// useful for progressive readv()'s filling the buffers.
|
// useful for progressive readv()'s filling the buffers.
|
||||||
const_iovec_view make_iov(const iovec_view &, const const_buffers &, const size_t &off = 0);
|
const_iovec_view make_iov(const iovec_view &, const const_buffers &, const size_t &off = 0);
|
||||||
const_iovec_view make_iov(const iovec_view &, const mutable_buffers &, const size_t &off = 0);
|
const_iovec_view make_iov(const iovec_view &, const mutable_buffers &, const size_t &off = 0);
|
||||||
|
|
||||||
|
// For boost::asio; internal
|
||||||
|
template<class T> vector_view<const T> make_iov(T *, const const_iovec_view &);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ namespace ircd::fs::support
|
||||||
extern const bool aio;
|
extern const bool aio;
|
||||||
extern const bool aio_fsync;
|
extern const bool aio_fsync;
|
||||||
extern const bool aio_fdsync;
|
extern const bool aio_fdsync;
|
||||||
|
extern const bool iou;
|
||||||
|
|
||||||
// Test if O_DIRECT supported at target path
|
// Test if O_DIRECT supported at target path
|
||||||
bool direct_io(const string_view &path);
|
bool direct_io(const string_view &path);
|
||||||
|
|
242
ircd/fs.cc
242
ircd/fs.cc
|
@ -65,16 +65,10 @@ noexcept
|
||||||
void
|
void
|
||||||
ircd::fs::init_dump_info()
|
ircd::fs::init_dump_info()
|
||||||
{
|
{
|
||||||
const bool support_async
|
if(unlikely(!support::aio && !support::iou))
|
||||||
{
|
|
||||||
false || aio::system
|
|
||||||
};
|
|
||||||
|
|
||||||
if(!support_async)
|
|
||||||
log::warning
|
log::warning
|
||||||
{
|
{
|
||||||
log, "Support for asynchronous filesystem IO has not been"
|
log, "Filesystem IO is degraded to synchronous system calls."
|
||||||
" established. Filesystem IO is degraded to synchronous system calls."
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -234,15 +228,20 @@ ircd::fs::support::aio
|
||||||
#endif
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
|
decltype(ircd::fs::support::iou)
|
||||||
|
ircd::fs::support::iou
|
||||||
|
{
|
||||||
|
#if IRCD_USE_ASIO_IO_URING == 1
|
||||||
|
info::kernel_version[0] > 5 ||
|
||||||
|
(info::kernel_version[0] >= 5 && info::kernel_version[1] >= 1)
|
||||||
|
#else
|
||||||
|
false
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
void
|
void
|
||||||
ircd::fs::support::dump_info()
|
ircd::fs::support::dump_info()
|
||||||
{
|
{
|
||||||
#if IRCD_USE_AIO
|
|
||||||
const bool support_async {true};
|
|
||||||
#else
|
|
||||||
const bool support_async {false};
|
|
||||||
#endif
|
|
||||||
|
|
||||||
char support[128] {0};
|
char support[128] {0};
|
||||||
const auto _append{[&support]
|
const auto _append{[&support]
|
||||||
(const string_view &name, const bool &avail, const int &enable)
|
(const string_view &name, const bool &avail, const int &enable)
|
||||||
|
@ -256,7 +255,8 @@ ircd::fs::support::dump_info()
|
||||||
});
|
});
|
||||||
}};
|
}};
|
||||||
|
|
||||||
_append("async", support_async, -1);
|
_append("iou", iou, IRCD_USE_ASIO_READ);
|
||||||
|
_append("aio", aio, -1);
|
||||||
_append("preadv2", preadv2, -1);
|
_append("preadv2", preadv2, -1);
|
||||||
_append("pwritev2", pwritev2, -1);
|
_append("pwritev2", pwritev2, -1);
|
||||||
_append("SYNC", sync, -1);
|
_append("SYNC", sync, -1);
|
||||||
|
@ -763,9 +763,18 @@ ircd::fs::flush(const fd &fd,
|
||||||
// fs/read.h
|
// fs/read.h
|
||||||
//
|
//
|
||||||
|
|
||||||
ircd::fs::read_opts
|
namespace ircd::fs
|
||||||
const ircd::fs::read_opts_default
|
{
|
||||||
{};
|
static int flags(const read_opts &);
|
||||||
|
static size_t _read_preadv2(const fd &, const const_iovec_view &, const read_opts &);
|
||||||
|
static size_t _read_preadv(const fd &, const const_iovec_view &, const read_opts &);
|
||||||
|
static size_t _read_asio(const fd &, const const_iovec_view &, const read_opts &);
|
||||||
|
static size_t _read(const fd &, const const_iovec_view &, const read_opts &);
|
||||||
|
static size_t _read_asio(const vector_view<read_op> &);
|
||||||
|
}
|
||||||
|
|
||||||
|
decltype(ircd::fs::read_opts_default)
|
||||||
|
ircd::fs::read_opts_default;
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
ircd::fs::prefetch(const fd &fd,
|
ircd::fs::prefetch(const fd &fd,
|
||||||
|
@ -888,10 +897,13 @@ ircd::fs::read(const vector_view<read_op> &op)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
#ifdef IRCD_USE_AIO
|
if constexpr(IRCD_USE_ASIO_READ)
|
||||||
if(likely(aio::system && aio && !all))
|
if(likely(support::iou && aio && !all))
|
||||||
return aio::read(op);
|
return _read_asio(op);
|
||||||
#endif
|
|
||||||
|
if constexpr(IRCD_USE_AIO)
|
||||||
|
if(likely(aio::system && aio && !all))
|
||||||
|
return aio::read(op);
|
||||||
|
|
||||||
// Fallback to sequential read operations
|
// Fallback to sequential read operations
|
||||||
size_t ret(0);
|
size_t ret(0);
|
||||||
|
@ -911,13 +923,43 @@ ircd::fs::read(const vector_view<read_op> &op)
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ircd::fs
|
#if IRCD_USE_ASIO_READ
|
||||||
|
size_t
|
||||||
|
ircd::fs::_read_asio(const vector_view<read_op> &op)
|
||||||
{
|
{
|
||||||
static int flags(const read_opts &opts);
|
const auto &ops(op.size());
|
||||||
static size_t _read_preadv2(const fd &, const const_iovec_view &, const read_opts &);
|
std::optional<asio::random_access_file> d[ops];
|
||||||
static size_t _read_preadv(const fd &, const const_iovec_view &, const read_opts &);
|
const unwind release{[&d]
|
||||||
static size_t _read(const fd &, const const_iovec_view &, const read_opts &);
|
{
|
||||||
|
for(auto &_d : d)
|
||||||
|
if(likely(_d))
|
||||||
|
_d->release();
|
||||||
|
}};
|
||||||
|
|
||||||
|
for(uint i(0); i < ops; ++i)
|
||||||
|
{
|
||||||
|
assert(op[i].fd);
|
||||||
|
d[i].emplace(ios::get(), int(*op[i].fd));
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t ret {0};
|
||||||
|
ctx::latch latch {ops};
|
||||||
|
for(uint i(0); i < ops; ++i)
|
||||||
|
d[i]->async_read_some_at(op[i].opts->offset, op[i].bufs, [i, &op, &ret, &latch]
|
||||||
|
(const auto &ec, const size_t &bytes)
|
||||||
|
{
|
||||||
|
if(ec && ec != net::eof)
|
||||||
|
op[i].eptr = make_system_eptr(ec);
|
||||||
|
|
||||||
|
op[i].ret = bytes;
|
||||||
|
ret += bytes;
|
||||||
|
latch.count_down();
|
||||||
|
});
|
||||||
|
|
||||||
|
latch.wait();
|
||||||
|
return ret;
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
/// Read from file descriptor fd into buffers. The number of bytes read into
|
/// Read from file descriptor fd into buffers. The number of bytes read into
|
||||||
/// the buffers is returned. By default (via read_opts.all) this call will
|
/// the buffers is returned. By default (via read_opts.all) this call will
|
||||||
|
@ -988,6 +1030,10 @@ ircd::fs::_read(const fd &fd,
|
||||||
{
|
{
|
||||||
assert(opts.op == op::READ);
|
assert(opts.op == op::READ);
|
||||||
|
|
||||||
|
if constexpr(IRCD_USE_ASIO_READ)
|
||||||
|
if(likely(support::iou && opts.aio))
|
||||||
|
return _read_asio(fd, iov, opts);
|
||||||
|
|
||||||
if constexpr(IRCD_USE_AIO)
|
if constexpr(IRCD_USE_AIO)
|
||||||
if(likely(aio::system && opts.aio))
|
if(likely(aio::system && opts.aio))
|
||||||
return aio::read(fd, iov, opts);
|
return aio::read(fd, iov, opts);
|
||||||
|
@ -1001,6 +1047,56 @@ ircd::fs::_read(const fd &fd,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if IRCD_USE_ASIO_READ
|
||||||
|
size_t
|
||||||
|
ircd::fs::_read_asio(const fd &fd,
|
||||||
|
const const_iovec_view &iov,
|
||||||
|
const read_opts &opts)
|
||||||
|
{
|
||||||
|
assert(bytes(iov) > 0);
|
||||||
|
assert(opts.offset >= 0);
|
||||||
|
|
||||||
|
asio::mutable_buffer buf[iov.size()];
|
||||||
|
const auto bufs
|
||||||
|
{
|
||||||
|
make_iov(buf, iov)
|
||||||
|
};
|
||||||
|
|
||||||
|
asio::random_access_file d
|
||||||
|
{
|
||||||
|
ios::get(), int(fd)
|
||||||
|
};
|
||||||
|
|
||||||
|
const unwind release{[&d]
|
||||||
|
{
|
||||||
|
d.release();
|
||||||
|
}};
|
||||||
|
|
||||||
|
const auto interruption{[&d, &opts]
|
||||||
|
(ctx::ctx *const &interruptor)
|
||||||
|
{
|
||||||
|
if(opts.interruptible)
|
||||||
|
d.cancel();
|
||||||
|
}};
|
||||||
|
|
||||||
|
boost::system::error_code ec;
|
||||||
|
size_t ret {0}; continuation
|
||||||
|
{
|
||||||
|
continuation::asio_predicate, interruption, [&ret, &d, &opts, &bufs, &ec]
|
||||||
|
(auto &yield)
|
||||||
|
{
|
||||||
|
ret = d.async_read_some_at(opts.offset, bufs, yield[ec]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
assert(ret || ec == net::eof);
|
||||||
|
if(unlikely(ec && ec != net::eof))
|
||||||
|
throw_system_error(ec);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
ircd::fs::_read_preadv(const fd &fd,
|
ircd::fs::_read_preadv(const fd &fd,
|
||||||
const const_iovec_view &iov,
|
const const_iovec_view &iov,
|
||||||
|
@ -1073,9 +1169,8 @@ ircd::fs::flags(const read_opts &opts)
|
||||||
// fs/write.h
|
// fs/write.h
|
||||||
//
|
//
|
||||||
|
|
||||||
ircd::fs::write_opts
|
decltype(ircd::fs::write_opts_default)
|
||||||
const ircd::fs::write_opts_default
|
ircd::fs::write_opts_default;
|
||||||
{};
|
|
||||||
|
|
||||||
void
|
void
|
||||||
ircd::fs::allocate(const fd &fd,
|
ircd::fs::allocate(const fd &fd,
|
||||||
|
@ -1254,6 +1349,15 @@ ircd::fs::append(const fd &fd,
|
||||||
// write
|
// write
|
||||||
//
|
//
|
||||||
|
|
||||||
|
namespace ircd::fs
|
||||||
|
{
|
||||||
|
static int flags(const write_opts &opts);
|
||||||
|
static size_t _write_pwritev2(const fd &, const const_iovec_view &, const write_opts &);
|
||||||
|
static size_t _write_pwritev(const fd &, const const_iovec_view &, const write_opts &);
|
||||||
|
static size_t _write_asio(const fd &, const const_iovec_view &, const write_opts &);
|
||||||
|
static size_t _write(const fd &, const const_iovec_view &, const write_opts &);
|
||||||
|
}
|
||||||
|
|
||||||
ircd::const_buffer
|
ircd::const_buffer
|
||||||
ircd::fs::write(const string_view &path,
|
ircd::fs::write(const string_view &path,
|
||||||
const const_buffer &buf,
|
const const_buffer &buf,
|
||||||
|
@ -1302,14 +1406,6 @@ ircd::fs::write(const string_view &path,
|
||||||
return write(fd, bufs, opts);
|
return write(fd, bufs, opts);
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace ircd::fs
|
|
||||||
{
|
|
||||||
static int flags(const write_opts &opts);
|
|
||||||
static size_t _write_pwritev2(const fd &, const const_iovec_view &, const write_opts &);
|
|
||||||
static size_t _write_pwritev(const fd &, const const_iovec_view &, const write_opts &);
|
|
||||||
static size_t _write(const fd &, const const_iovec_view &, const write_opts &);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wstack-usage="
|
#pragma GCC diagnostic ignored "-Wstack-usage="
|
||||||
size_t
|
size_t
|
||||||
|
@ -1363,6 +1459,10 @@ ircd::fs::_write(const fd &fd,
|
||||||
{
|
{
|
||||||
assert(opts.op == op::WRITE);
|
assert(opts.op == op::WRITE);
|
||||||
|
|
||||||
|
if constexpr(IRCD_USE_ASIO_WRITE)
|
||||||
|
if(likely(support::iou && opts.aio))
|
||||||
|
return _write_asio(fd, iov, opts);
|
||||||
|
|
||||||
if constexpr(IRCD_USE_AIO)
|
if constexpr(IRCD_USE_AIO)
|
||||||
if(likely(aio::system && opts.aio))
|
if(likely(aio::system && opts.aio))
|
||||||
return aio::write(fd, iov, opts);
|
return aio::write(fd, iov, opts);
|
||||||
|
@ -1376,6 +1476,55 @@ ircd::fs::_write(const fd &fd,
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if IRCD_USE_ASIO_WRITE
|
||||||
|
size_t
|
||||||
|
ircd::fs::_write_asio(const fd &fd,
|
||||||
|
const const_iovec_view &iov,
|
||||||
|
const write_opts &opts)
|
||||||
|
{
|
||||||
|
assert(bytes(iov) > 0);
|
||||||
|
assert(opts.offset >= 0 || opts.offset == -1);
|
||||||
|
|
||||||
|
asio::const_buffer buf[iov.size()];
|
||||||
|
const auto bufs
|
||||||
|
{
|
||||||
|
make_iov(buf, iov)
|
||||||
|
};
|
||||||
|
|
||||||
|
asio::random_access_file d
|
||||||
|
{
|
||||||
|
ios::get(), int(fd)
|
||||||
|
};
|
||||||
|
|
||||||
|
const unwind release{[&d]
|
||||||
|
{
|
||||||
|
d.release();
|
||||||
|
}};
|
||||||
|
|
||||||
|
const auto interruption{[&d, &opts]
|
||||||
|
(ctx::ctx *const &interruptor)
|
||||||
|
{
|
||||||
|
if(opts.interruptible)
|
||||||
|
d.cancel();
|
||||||
|
}};
|
||||||
|
|
||||||
|
boost::system::error_code ec;
|
||||||
|
size_t ret {0}; continuation
|
||||||
|
{
|
||||||
|
continuation::asio_predicate, interruption, [&ret, &d, &opts, &bufs, &ec]
|
||||||
|
(auto &yield)
|
||||||
|
{
|
||||||
|
ret = d.async_write_some_at(opts.offset, bufs, yield[ec]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
if(unlikely(ec))
|
||||||
|
throw_system_error(ec);
|
||||||
|
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
size_t
|
size_t
|
||||||
ircd::fs::_write_pwritev(const fd &fd,
|
ircd::fs::_write_pwritev(const fd &fd,
|
||||||
const const_iovec_view &iov,
|
const const_iovec_view &iov,
|
||||||
|
@ -2608,6 +2757,23 @@ ircd::fs::aio::translate(const int &val)
|
||||||
// fs/iov.h
|
// fs/iov.h
|
||||||
//
|
//
|
||||||
|
|
||||||
|
template<class T>
|
||||||
|
ircd::vector_view<const T>
|
||||||
|
ircd::fs::make_iov(T *const buf,
|
||||||
|
const const_iovec_view &iov)
|
||||||
|
{
|
||||||
|
for(size_t i(0); i < iov.size(); ++i)
|
||||||
|
buf[i] = T
|
||||||
|
{
|
||||||
|
iov[i].iov_base, iov[i].iov_len
|
||||||
|
};
|
||||||
|
|
||||||
|
return vector_view<const T>
|
||||||
|
{
|
||||||
|
buf, iov.size()
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
ircd::fs::const_iovec_view
|
ircd::fs::const_iovec_view
|
||||||
ircd::fs::make_iov(const iovec_view &iov,
|
ircd::fs::make_iov(const iovec_view &iov,
|
||||||
const mutable_buffers &bufs,
|
const mutable_buffers &bufs,
|
||||||
|
|
Loading…
Reference in a new issue