io/uring: basic Linux io_uring support

This commit is contained in:
Max Kellermann 2020-05-05 14:28:15 +02:00
parent 935e622915
commit 62d0ceabcc
11 changed files with 638 additions and 0 deletions

View File

@ -334,6 +334,7 @@ endif
subdir('src/util')
subdir('src/time')
subdir('src/io')
subdir('src/io/uring')
subdir('src/system')
subdir('src/thread')
subdir('src/net')

View File

@ -4,6 +4,7 @@ option('test', type: 'boolean', value: false, description: 'Build the unit tests
option('syslog', type: 'feature', description: 'syslog support')
option('inotify', type: 'boolean', value: true, description: 'inotify support (for automatic database update)')
option('io_uring', type: 'feature', description: 'Linux io_uring support using liburing')
option('daemon', type: 'boolean', value: true, description: 'enable daemonization')
option('systemd', type: 'feature', description: 'systemd support')

View File

@ -0,0 +1,81 @@
/*
* Copyright 2020 CM4all GmbH
* All rights reserved.
*
* author: Max Kellermann <mk@cm4all.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "Operation.hxx"
#include <boost/intrusive/list_hook.hpp>
#include <cassert>
#include <utility>
namespace Uring {
class CancellableOperation
: public boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>>
{
Operation *operation;
public:
CancellableOperation(Operation &_operation) noexcept
:operation(&_operation)
{
assert(operation->cancellable == nullptr);
operation->cancellable = this;
}
~CancellableOperation() noexcept {
assert(operation == nullptr);
}
void Cancel(Operation &_operation) noexcept {
(void)_operation;
assert(operation == &_operation);
operation = nullptr;
// TODO: io_uring_prep_cancel()
}
void OnUringCompletion(int res) noexcept {
if (operation == nullptr)
return;
assert(operation->cancellable == this);
operation->cancellable = nullptr;
std::exchange(operation, nullptr)->OnUringCompletion(res);
}
};
} // namespace Uring

49
src/io/uring/Handler.hxx Normal file
View File

@ -0,0 +1,49 @@
/*
* Copyright 2020 CM4all GmbH
* All rights reserved.
*
* author: Max Kellermann <mk@cm4all.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include <exception>
struct statx;
class UniqueFileDescriptor;
namespace Uring {
class OpenStatHandler {
public:
virtual void OnOpenStat(UniqueFileDescriptor fd,
struct statx &st) noexcept = 0;
virtual void OnOpenStatError(std::exception_ptr e) noexcept = 0;
};
} // namespace Uring

View File

@ -0,0 +1,47 @@
/*
* Copyright 2020 CM4all GmbH
* All rights reserved.
*
* author: Max Kellermann <mk@cm4all.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "Operation.hxx"
#include "CancellableOperation.hxx"
namespace Uring {
void
Operation::CancelUring() noexcept
{
if (cancellable == nullptr)
return;
std::exchange(cancellable, nullptr)->Cancel(*this);
}
} // namespace Uring

View File

@ -0,0 +1,58 @@
/*
* Copyright 2020 CM4all GmbH
* All rights reserved.
*
* author: Max Kellermann <mk@cm4all.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
namespace Uring {
class CancellableOperation;
class Operation {
friend class CancellableOperation;
CancellableOperation *cancellable = nullptr;
public:
~Operation() noexcept {
CancelUring();
}
bool IsUringPending() const noexcept {
return cancellable != nullptr;
}
void CancelUring() noexcept;
virtual void OnUringCompletion(int res) noexcept = 0;
};
} // namespace Uring

97
src/io/uring/Queue.cxx Normal file
View File

@ -0,0 +1,97 @@
/*
* Copyright 2020 CM4all GmbH
* All rights reserved.
*
* author: Max Kellermann <mk@cm4all.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "Queue.hxx"
#include "CancellableOperation.hxx"
#include "Operation.hxx"
#include "util/DeleteDisposer.hxx"
#include <cassert>
namespace Uring {
Queue::Queue(unsigned entries, unsigned flags)
:ring(entries, flags)
{
}
Queue::~Queue() noexcept
{
operations.clear_and_dispose(DeleteDisposer{});
}
void
Queue::AddPending(struct io_uring_sqe &sqe,
Operation &operation) noexcept
{
auto *c = new CancellableOperation(operation);
operations.push_back(*c);
io_uring_sqe_set_data(&sqe, c);
}
void
Queue::DispatchOneCompletion(struct io_uring_cqe &cqe) noexcept
{
void *data = io_uring_cqe_get_data(&cqe);
if (data != nullptr) {
auto *c = (CancellableOperation *)data;
c->OnUringCompletion(cqe.res);
operations.erase_and_dispose(operations.iterator_to(*c),
DeleteDisposer{});
}
ring.SeenCompletion(cqe);
}
bool
Queue::DispatchOneCompletion()
{
auto *cqe = ring.PeekCompletion();
if (cqe == nullptr)
return false;
DispatchOneCompletion(*cqe);
return true;
}
bool
Queue::WaitDispatchOneCompletion()
{
auto *cqe = ring.WaitCompletion();
if (cqe == nullptr)
return false;
DispatchOneCompletion(*cqe);
return true;
}
} // namespace Uring

104
src/io/uring/Queue.hxx Normal file
View File

@ -0,0 +1,104 @@
/*
* Copyright 2020 CM4all GmbH
* All rights reserved.
*
* author: Max Kellermann <mk@cm4all.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "Ring.hxx"
#include <liburing.h>
#include <boost/intrusive/list.hpp>
namespace Uring {
class Operation;
class CancellableOperation;
/**
* High-level C++ wrapper for a `struct io_uring`. It supports a
* handler class, cancellation, ...
*/
class Queue {
Ring ring;
boost::intrusive::list<CancellableOperation,
boost::intrusive::base_hook<boost::intrusive::list_base_hook<boost::intrusive::link_mode<boost::intrusive::normal_link>>>,
boost::intrusive::constant_time_size<false>> operations;
public:
Queue(unsigned entries, unsigned flags);
~Queue() noexcept;
FileDescriptor GetFileDescriptor() const noexcept {
return ring.GetFileDescriptor();
}
struct io_uring_sqe *GetSubmitEntry() noexcept {
return ring.GetSubmitEntry();
}
bool HasPending() const noexcept {
return !operations.empty();
}
protected:
void AddPending(struct io_uring_sqe &sqe,
Operation &operation) noexcept;
public:
virtual void Push(struct io_uring_sqe &sqe,
Operation &operation) noexcept {
AddPending(sqe, operation);
Submit();
}
void Submit() {
ring.Submit();
}
bool DispatchOneCompletion();
void DispatchCompletions() {
while (DispatchOneCompletion()) {}
}
bool WaitDispatchOneCompletion();
void WaitDispatchCompletions() {
while (WaitDispatchOneCompletion()) {}
}
private:
void DispatchOneCompletion(struct io_uring_cqe &cqe) noexcept;
};
} // namespace Uring

83
src/io/uring/Ring.cxx Normal file
View File

@ -0,0 +1,83 @@
/*
* Copyright 2020 CM4all GmbH
* All rights reserved.
*
* author: Max Kellermann <mk@cm4all.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "Ring.hxx"
#include "system/Error.hxx"
namespace Uring {
Ring::Ring(unsigned entries, unsigned flags)
{
int error = io_uring_queue_init(entries, &ring, flags);
if (error < 0)
throw MakeErrno(-error, "io_uring_queue_init() failed");
}
void
Ring::Submit()
{
int error = io_uring_submit(&ring);
if (error < 0)
throw MakeErrno(-error, "io_uring_submit() failed");
}
struct io_uring_cqe *
Ring::WaitCompletion()
{
struct io_uring_cqe *cqe;
int error = io_uring_wait_cqe(&ring, &cqe);
if (error < 0) {
if (error == -EAGAIN)
return nullptr;
throw MakeErrno(-error, "io_uring_wait_cqe() failed");
}
return cqe;
}
struct io_uring_cqe *
Ring::PeekCompletion()
{
struct io_uring_cqe *cqe;
int error = io_uring_peek_cqe(&ring, &cqe);
if (error < 0) {
if (error == -EAGAIN)
return nullptr;
throw MakeErrno(-error, "io_uring_peek_cqe() failed");
}
return cqe;
}
} // namespace Uring

81
src/io/uring/Ring.hxx Normal file
View File

@ -0,0 +1,81 @@
/*
* Copyright 2020 CM4all GmbH
* All rights reserved.
*
* author: Max Kellermann <mk@cm4all.com>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the
* distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* FOUNDATION OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
* STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
* OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#pragma once
#include "io/FileDescriptor.hxx"
#include <liburing.h>
namespace Uring {
/**
* Low-level C++ wrapper for a `struct io_uring`. It provides simple
* wrappers to liburing functions and throws std::system_error on
* errors.
*/
class Ring {
struct io_uring ring;
public:
Ring(unsigned entries, unsigned flags);
~Ring() noexcept {
io_uring_queue_exit(&ring);
}
Ring(const Ring &) = delete;
Ring &operator=(const Ring &) = delete;
FileDescriptor GetFileDescriptor() const noexcept {
return FileDescriptor(ring.ring_fd);
}
struct io_uring_sqe *GetSubmitEntry() noexcept {
return io_uring_get_sqe(&ring);
}
void Submit();
struct io_uring_cqe *WaitCompletion();
/**
* @return a completion queue entry or nullptr on EAGAIN
*/
struct io_uring_cqe *PeekCompletion();
void SeenCompletion(struct io_uring_cqe &cqe) noexcept {
io_uring_cqe_seen(&ring, &cqe);
}
};
} // namespace Uring

36
src/io/uring/meson.build Normal file
View File

@ -0,0 +1,36 @@
uring_features = configuration_data()
if host_machine.system() != 'linux'
uring_dep = dependency('', required: false)
uring_features.set('HAVE_URING', false)
configure_file(output: 'Features.h', configuration: uring_features)
subdir_done()
endif
liburing = dependency('liburing', required: get_option('io_uring'))
uring_features.set('HAVE_URING', liburing.found())
configure_file(output: 'Features.h', configuration: uring_features)
if not liburing.found()
uring_dep = dependency('', required: false)
subdir_done()
endif
uring = static_library(
'uring',
'Ring.cxx',
'Queue.cxx',
'Operation.cxx',
include_directories: inc,
dependencies: [
liburing,
],
)
uring_dep = declare_dependency(
link_with: uring,
dependencies: [
liburing,
io_dep,
],
)