CMakeLists.txt | 31 +++
cmake/Findspdk.cmake | 114 ++++++++++
cmake/SeastarDependencies.cmake | 10 +
configure.py | 9 +
cooking_recipe.cmake | 11 +
demos/CMakeLists.txt | 3 +
demos/spdk_bdev_demo.cc | 90 ++++++++
include/seastar/core/smp.hh | 3 +-
include/seastar/core/spdk_app.hh | 54 +++++
include/seastar/core/spdk_bdev.hh | 57 +++++
include/seastar/core/spdk_lib.hh | 33 +++
include/seastar/core/spdk_thread.hh | 100 +++++++++
src/core/app-template.cc | 5 +-
src/core/reactor.cc | 21 ++
src/core/spdk_app.cc | 310 ++++++++++++++++++++++++++++
src/core/spdk_bdev.cc | 158 ++++++++++++++
src/core/spdk_lib.cc | 39 ++++
src/core/spdk_thread.cc | 158 ++++++++++++++
18 files changed, 1204 insertions(+), 2 deletions(-)
create mode 100644 cmake/Findspdk.cmake
create mode 100644 demos/spdk_bdev_demo.cc
create mode 100644 include/seastar/core/spdk_app.hh
create mode 100644 include/seastar/core/spdk_bdev.hh
create mode 100644 include/seastar/core/spdk_lib.hh
create mode 100644 include/seastar/core/spdk_thread.hh
create mode 100644 src/core/spdk_app.cc
create mode 100644 src/core/spdk_bdev.cc
create mode 100644 src/core/spdk_lib.cc
create mode 100644 src/core/spdk_thread.cc
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 4248aad8..f2886840 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -206,6 +206,10 @@ option (Seastar_DPDK
"Enable DPDK support."
OFF)
+option (Seastar_SPDK
+ "Enable SPDK support."
+ OFF)
+
option (Seastar_EXCLUDE_APPS_FROM_ALL
"When enabled alongside Seastar_APPS, do not build applications by default."
OFF)
@@ -494,6 +498,10 @@ add_library (seastar STATIC
include/seastar/core/simple-stream.hh
include/seastar/core/slab.hh
include/seastar/core/sleep.hh
+ include/seastar/core/spdk_app.hh
+ include/seastar/core/spdk_bdev.hh
+ include/seastar/core/spdk_lib.hh
+ include/seastar/core/spdk_thread.hh
include/seastar/core/sstring.hh
include/seastar/core/stall_sampler.hh
include/seastar/core/stream.hh
@@ -623,6 +631,10 @@ add_library (seastar STATIC
src/core/scollectd-impl.hh
src/core/systemwide_memory_barrier.cc
src/core/smp.cc
+ src/core/spdk_app.cc
+ src/core/spdk_bdev.cc
+ src/core/spdk_lib.cc
+ src/core/spdk_thread.cc
src/core/sstring.cc
src/core/thread.cc
src/core/uname.cc
@@ -730,6 +742,17 @@ if (Seastar_DPDK)
PRIVATE
dpdk::dpdk)
endif()
+if (Seastar_SPDK)
+ target_link_libraries (seastar
+ PRIVATE
+ spdk::event_bdev
+ spdk::event_accel
+ spdk::bdev
+ spdk::accel
+ spdk::init
+ spdk::env_dpdk
+ dpdk::dpdk)
+endif()
set (Seastar_SANITIZE_MODES "Debug" "Sanitize")
if ((Seastar_SANITIZE STREQUAL "ON") OR
@@ -884,6 +907,13 @@ if (Seastar_DPDK)
PUBLIC $<TARGET_PROPERTY:dpdk::dpdk,INTERFACE_INCLUDE_DIRECTORIES>)
endif ()
+if (Seastar_SPDK)
+ target_compile_definitions (seastar
+ PUBLIC SEASTAR_HAVE_SPDK)
+ target_link_libraries (seastar
+ PUBLIC spdk::spdk)
+endif ()
+
if (Seastar_HWLOC)
if (NOT hwloc_FOUND)
message (FATAL_ERROR "`hwloc` support is enabled but it is not available!")
@@ -1210,6 +1240,7 @@ if (Seastar_INSTALL)
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Findlksctp-tools.cmake
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Findlz4.cmake
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Findnumactl.cmake
+ ${CMAKE_CURRENT_SOURCE_DIR}/cmake/Findspdk.cmake
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Findragel.cmake
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Findrt.cmake
${CMAKE_CURRENT_SOURCE_DIR}/cmake/Findyaml-cpp.cmake
diff --git a/cmake/Findspdk.cmake b/cmake/Findspdk.cmake
new file mode 100644
index 00000000..6ce9bd8b
--- /dev/null
+++ b/cmake/Findspdk.cmake
@@ -0,0 +1,114 @@
+#
+# This file is open source software, licensed to you under the terms
+# of the Apache License, Version 2.0 (the "License"). See the NOTICE file
+# distributed with this work for additional information regarding copyright
+# ownership. You may not use this file except in compliance with the License.
+#
+# You may obtain a copy of the License at
+#
+#
http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing,
+# software distributed under the License is distributed on an
+# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+# KIND, either express or implied. See the License for the
+# specific language governing permissions and limitations
+# under the License.
+#
+
+#
+# Copyright (C) 2021 Kefu Chai <
tcha...@gmail.com>
+#
+
+find_package (PkgConfig REQUIRED)
+
+if(spdk_FIND_COMPONENTS)
+ if(NOT bdev IN_LIST spdk_FIND_COMPONENTS)
+ list (APPEND spdk_FIND_COMPONENTS bdev)
+ endif()
+else()
+ set(spdk_FIND_COMPONENTS
+ bdev
+ blobfs
+ env_dpdk
+ event
+ ftl
+ iscsi
+ json
+ jsonrpc
+ log
+ lvol
+ nvme
+ syslibs
+ thread
+ vhost)
+endif()
+
+include (FindPackageHandleStandardArgs)
+set (spdk_INCLUDE_DIR)
+set (spdk_LINK_DIRECTORIES)
+
+set(_spdk_bdev_aio_deps aio)
+set(_spdk_util_deps uuid)
+
+foreach (component ${spdk_FIND_COMPONENTS})
+ pkg_check_modules (spdk_PC spdk_${component})
+ add_library (spdk::${component} INTERFACE IMPORTED)
+ set (prefix spdk_PC_STATIC)
+ foreach (spdk_lib bdev_aio util)
+ foreach (dep ${_spdk_${spdk_lib}_deps})
+ find_package (${dep} QUIET REQUIRED)
+ list (APPEND ${prefix}_LIBRARIES ${dep})
+ endforeach ()
+ endforeach ()
+ set_target_properties (spdk::${component}
+ PROPERTIES
+ INTERFACE_COMPILE_OPTIONS ${${prefix}_CFLAGS}
+ INTERFACE_INCLUDE_DIRECTORIES ${${prefix}_INCLUDE_DIRS}
+ INTERFACE_LINK_OPTIONS "-Wl,--whole-archive;${${prefix}_LDFLAGS};-Wl,--no-whole-archive"
+ INTERFACE_LINK_LIBRARIES "${${prefix}_LIBRARIES}"
+ INTERFACE_LINK_DIRECTORIES ${${prefix}_LIBRARY_DIRS})
+ if (NOT spdk_INCLUDE_DIR)
+ set (spdk_INCLUDE_DIR ${${prefix}_INCLUDE_DIRS})
+ endif ()
+ if (NOT spdk_LINK_DIRECTORIES)
+ set (spdk_LINK_DIRECTORIES ${${prefix}_LIBRARY_DIRS})
+ endif ()
+ list (APPEND spdk_link_opts "${${prefix}_LDFLAGS}")
+ list (APPEND spdk_libs ${${prefix}_LIBRARIES})
+ list (APPEND spdk_lib_vars ${prefix}_LIBRARIES)
+endforeach ()
+
+if (spdk_INCLUDE_DIR AND EXISTS "${spdk_INCLUDE_DIR}/spdk/version.h")
+ foreach(ver "MAJOR" "MINOR" "PATCH")
+ file(STRINGS "${spdk_INCLUDE_DIR}/spdk/version.h" spdk_VER_${ver}_LINE
+ REGEX "^#define[ \t ]+SPDK_VERSION_${ver}[ \t]+[0-9]+$")
+ string(REGEX REPLACE "^#define[ \t]+SPDK_VERSION_${ver}[ \t]+([0-9]+)$"
+ "\\1" spdk_VERSION_${ver} "${spdk_VER_${ver}_LINE}")
+ unset(${spdk_VER_${ver}_LINE})
+ endforeach()
+ set(spdk_VERSION_STRING
+ "${spdk_VERSION_MAJOR}.${spdk_VERSION_MINOR}.${spdk_VERSION_PATCH}")
+endif ()
+
+find_package_handle_standard_args (spdk
+ REQUIRED_VARS
+ spdk_INCLUDE_DIR
+ spdk_LINK_DIRECTORIES
+ ${spdk_lib_vars}
+ VERSION_VAR
+ spdk_VERSION_STRING)
+
+if (spdk_FOUND AND NOT (TARGET spdk::spdk))
+ set (spdk_LIBRARIES ${spdk_libs})
+ set (whole_archive_link_opts
+ -Wl,--whole-archive -Wl,-Bstatic ${spdk_link_opts} -Wl,--no-whole-archive -Wl,-Bdynamic)
+ add_library (spdk::spdk INTERFACE IMPORTED)
+ set_target_properties (spdk::spdk
+ PROPERTIES
+ INTERFACE_COMPILE_OPTIONS "${spdk_PC_STATIC_bdev_CFLAGS}"
+ INTERFACE_INCLUDE_DIRECTORIES "${spdk_INCLUDE_DIR}"
+ INTERFACE_LINK_OPTIONS "${whole_archive_link_opts}"
+ INTERFACE_LINK_LIBRARIES "${spdk_LIBRARIES}"
+ INTERFACE_LINK_DIRECTORIES "${spdk_LINK_DIRECTORIES}")
+endif ()
diff --git a/cmake/SeastarDependencies.cmake b/cmake/SeastarDependencies.cmake
index 51a8a65a..07358c3d 100644
--- a/cmake/SeastarDependencies.cmake
+++ b/cmake/SeastarDependencies.cmake
@@ -53,6 +53,7 @@ macro (seastar_find_dependencies)
c-ares
cryptopp
dpdk # No version information published.
+ spdk
fmt
lz4
# Private and private/public dependencies.
@@ -92,6 +93,15 @@ macro (seastar_find_dependencies)
set (_seastar_dep_args_lksctp-tools REQUIRED)
set (_seastar_dep_args_rt REQUIRED)
set (_seastar_dep_args_yaml-cpp 0.5.1 REQUIRED)
+ set (_seastar_dep_args_spdk
+ 21.10.0
+ COMPONENTS
+ event_bdev
+ event_accel
+ bdev
+ accel
+ init
+ env_dpdk)
foreach (third_party ${_seastar_all_dependencies})
find_package ("${third_party}" ${_seastar_dep_args_${third_party}})
diff --git a/configure.py b/configure.py
index 27a40fd9..5767455d 100755
--- a/configure.py
+++ b/configure.py
@@ -81,6 +81,11 @@ add_tristate(
name = 'dpdk',
dest = 'dpdk',
help = 'DPDK support')
+add_tristate(
+ arg_parser,
+ name = 'spdk',
+ dest = 'spdk',
+ help = 'SPDK support')
add_tristate(
arg_parser,
name = 'hwloc',
@@ -189,6 +194,7 @@ def configure_mode(mode):
tr(LDFLAGS, 'LD_FLAGS'),
tr(args.cpp_dialect, 'CXX_DIALECT'),
tr(args.dpdk, 'DPDK'),
+ tr(args.spdk, 'SPDK'),
tr(infer_dpdk_machine(args.user_cflags), 'DPDK_MACHINE'),
tr(args.hwloc, 'HWLOC', value_when_none='yes'),
tr(args.alloc_failure_injection, 'ALLOC_FAILURE_INJECTION', value_when_none='DEFAULT'),
@@ -206,6 +212,9 @@ def configure_mode(mode):
if args.dpdk:
ingredients_to_cook.add('dpdk')
+ if args.spdk:
+ ingredients_to_cook.add('spdk')
+
# Generate a new build by pointing to the source directory.
if ingredients_to_cook:
# We need to use cmake-cooking for some dependencies.
diff --git a/cooking_recipe.cmake b/cooking_recipe.cmake
index b098132e..a2f5c566 100644
--- a/cooking_recipe.cmake
+++ b/cooking_recipe.cmake
@@ -299,3 +299,14 @@ cooking_ingredient (lz4
CONFIGURE_COMMAND <DISABLE>
BUILD_COMMAND <DISABLE>
INSTALL_COMMAND ${make_command} PREFIX=<INSTALL_DIR> install)
+
+cooking_ingredient (spdk
+ EXTERNAL_PROJECT_ARGS
+ SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/spdk
+ BUILD_IN_SOURCE ON
+ CONFIGURE_COMMAND
+ <SOURCE_DIR>/configure --with-dpdk --without-isal --disable-tests --disable-unit-tests --disable-examples --disable-apps --prefix=<INSTALL_DIR>
+ BUILD_COMMAND
+ ${make_command}
+ INSTALL_COMMAND
+ ${make_command} install)
diff --git a/demos/CMakeLists.txt b/demos/CMakeLists.txt
index 084e8298..245c98d9 100644
--- a/demos/CMakeLists.txt
+++ b/demos/CMakeLists.txt
@@ -111,5 +111,8 @@ seastar_add_demo (sharded_parameter
seastar_add_demo (file
SOURCES file_demo.cc)
+seastar_add_demo (spdk_bdev
+ SOURCES spdk_bdev_demo.cc)
+
seastar_add_demo (tutorial_examples
SOURCES tutorial_examples.cc)
diff --git a/demos/spdk_bdev_demo.cc b/demos/spdk_bdev_demo.cc
new file mode 100644
index 00000000..c03d8ad4
--- /dev/null
+++ b/demos/spdk_bdev_demo.cc
@@ -0,0 +1,90 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:4; indent-tabs-mode:nil -*-
+/*
+ * This file is open source software, licensed to you under the terms
+ * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
+ * distributed with this work for additional information regarding copyright
+ * ownership. You may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Copyright (C) 2021 Kefu Chai <
tcha...@gmail.com>
+ */
+
+#include <cstring>
+#include <limits>
+#include <random>
+
+#include <seastar/core/app-template.hh>
+
+#include <seastar/core/aligned_buffer.hh>
+#include <seastar/core/file.hh>
+#include <seastar/core/fstream.hh>
+#include <seastar/core/seastar.hh>
+#include <seastar/core/spdk_app.hh>
+#include <seastar/core/spdk_bdev.hh>
+#include <seastar/core/spdk_lib.hh>
+#include <seastar/core/sstring.hh>
+#include <seastar/core/temporary_buffer.hh>
+#include <seastar/core/loop.hh>
+#include <seastar/core/io_intent.hh>
+#include <seastar/util/log.hh>
+#include <seastar/util/tmp_file.hh>
+
+using namespace seastar;
+namespace bpo = boost::program_options;
+
+seastar::logger spdk_logger("spdk_demo");
+
+int main(int ac, char** av) {
+ seastar::app_template seastar_app;
+ seastar_app.add_positional_options({
+ { "bdev", bpo::value<std::string>()->default_value("Malloc0"),
+ "bdev", 1 },
+ });
+ spdk::app spdk_app;
+
spdk_logger.info("app run");
+ return seastar_app.run(ac, av, [&] {
+
spdk_logger.info("demo running");
+ auto bdev_name = seastar_app.configuration()["bdev"].as<std::string>();
+ return spdk_app.run(seastar_app.configuration(), [bdev_name] {
+
spdk_logger.info("bdev.open");
+ auto bdev = spdk::block_device::open(bdev_name);
+ uint32_t block_size = bdev->block_size();
+ size_t buf_align = bdev->memory_dma_alignment();
+ auto buf = spdk::dma_zmalloc(block_size, buf_align);
+ return do_with(temporary_buffer<char>(std::move(buf)),
+ std::unique_ptr<spdk::block_device>(std::move(bdev)),
+ [] (temporary_buffer<char>& buf,
+ std::unique_ptr<spdk::block_device>& bdev) {
+
spdk_logger.info("bdev.write");
+ return bdev->write(0, buf.get(), buf.size()).then([&] {
+
spdk_logger.info("bdev.read");
+ memset(buf.get_write(), 0xff, buf.size());
+ return bdev->read(0, buf.get_write(), buf.size());
+ }).then([&buf] {
+
spdk_logger.info("bdev.read");
+ temporary_buffer<char> good{buf.size()};
+ memset(good.get_write(), 0, good.size());
+ if (int where = memcmp(good.get(), buf.get(), buf.size());
+ where != 0) {
+ spdk_logger.error("buf mismatches at {}!", where);
+ } else {
+
spdk_logger.info("buf matches!");
+ }
+ });
+ }).handle_exception_type([&] (std::system_error& e) {
+ spdk_logger.error("error while writing/reading {}", e.what());
+ });
+ });
+ });
+}
diff --git a/include/seastar/core/smp.hh b/include/seastar/core/smp.hh
index 1f58a08d..ab3e86b7 100644
--- a/include/seastar/core/smp.hh
+++ b/include/seastar/core/smp.hh
@@ -292,7 +292,7 @@ class smp_message_queue {
class smp : public std::enable_shared_from_this<smp> {
alien::instance& _alien;
std::vector<posix_thread> _threads;
- std::vector<std::function<void ()>> _thread_loops; // for dpdk
+ std::vector<std::function<void ()>> _thread_loops; // for dpdk/spdk
std::optional<boost::barrier> _all_event_loops_done;
struct qs_deleter {
void operator()(smp_message_queue** qs) const;
@@ -301,6 +301,7 @@ class smp : public std::enable_shared_from_this<smp> {
static thread_local smp_message_queue**_qs;
static thread_local std::thread::id _tmain;
bool _using_dpdk = false;
+ bool _using_spdk = false;
template <typename Func>
using returns_future = is_future<std::result_of_t<Func()>>;
diff --git a/include/seastar/core/spdk_app.hh b/include/seastar/core/spdk_app.hh
new file mode 100644
index 00000000..041c16ee
--- /dev/null
+++ b/include/seastar/core/spdk_app.hh
@@ -0,0 +1,54 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:4; indent-tabs-mode:nil -*-
+/*
+ * This file is open source software, licensed to you under the terms
+ * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
+ * distributed with this work for additional information regarding copyright
+ * ownership. You may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Copyright (C) 2021 Kefu Chai <
tcha...@gmail.com>
+ */
+
+#pragma once
+
+#include <boost/program_options.hpp>
+#include <seastar/core/future.hh>
+#include <seastar/core/resource.hh>
+#include <seastar/core/sharded.hh>
+#include <seastar/core/spdk_thread.hh>
+
+struct spdk_thread;
+
+namespace seastar::spdk {
+
+namespace env {
+ void start(const std::vector<resource::cpu>& cpuset,
+ const boost::program_options::variables_map& opts);
+ void stop() noexcept;
+};
+
+class app {
+public:
+ future<> run(const boost::program_options::variables_map& opts,
+ std::function<future<> ()>&& func) noexcept;
+ static boost::program_options::options_description get_options_description();
+private:
+ future<> start(const boost::program_options::variables_map& opts);
+ future<> stop();
+private:
+ sharded<executor> sharded_executor;
+ spdk_thread* app_thread = nullptr;
+};
+
+}
diff --git a/include/seastar/core/spdk_bdev.hh b/include/seastar/core/spdk_bdev.hh
new file mode 100644
index 00000000..0c10f70f
--- /dev/null
+++ b/include/seastar/core/spdk_bdev.hh
@@ -0,0 +1,57 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:4; indent-tabs-mode:nil -*-
+/*
+ * This file is open source software, licensed to you under the terms
+ * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
+ * distributed with this work for additional information regarding copyright
+ * ownership. You may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Copyright (C) 2021 Kefu Chai <
tcha...@gmail.com>
+ */
+
+#pragma once
+
+#include <memory>
+#include <seastar/core/future.hh>
+
+struct spdk_bdev;
+struct spdk_bdev_desc;
+struct spdk_io_channel;
+
+namespace seastar::spdk {
+
+class block_device {
+public:
+ static std::unique_ptr<block_device> open(const std::string& bdev_name);
+ ~block_device();
+
+ future<> write(uint64_t pos, const void* buffer, size_t len);
+ future<> read(uint64_t pos, void* buffer, size_t len);
+
+ uint32_t block_size() const;
+ size_t memory_dma_alignment() const;
+
+private:
+ block_device() = default;
+ static void event_cb(int /* spdk_bdev_event_type */ type,
+ struct spdk_bdev* bdev,
+ void* event_ctx);
+
+private:
+ spdk_bdev* bdev = nullptr;
+ spdk_bdev_desc* desc = nullptr;
+ spdk_io_channel* io_channel = nullptr;
+};
+
+}
diff --git a/include/seastar/core/spdk_lib.hh b/include/seastar/core/spdk_lib.hh
new file mode 100644
index 00000000..ef9f818c
--- /dev/null
+++ b/include/seastar/core/spdk_lib.hh
@@ -0,0 +1,33 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:4; indent-tabs-mode:nil -*-
+/*
+ * This file is open source software, licensed to you under the terms
+ * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
+ * distributed with this work for additional information regarding copyright
+ * ownership. You may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Copyright (C) 2021 Kefu Chai <
tcha...@gmail.com>
+ */
+
+#pragma once
+
+#include <bitset>
+#include <seastar/core/future.hh>
+#include <boost/program_options.hpp>
+
+namespace seastar::spdk {
+
+temporary_buffer<char> dma_zmalloc(size_t size, size_t align);
+
+}
diff --git a/include/seastar/core/spdk_thread.hh b/include/seastar/core/spdk_thread.hh
new file mode 100644
index 00000000..670af3ba
--- /dev/null
+++ b/include/seastar/core/spdk_thread.hh
@@ -0,0 +1,100 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:4; indent-tabs-mode:nil -*-
+/*
+ * This file is open source software, licensed to you under the terms
+ * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
+ * distributed with this work for additional information regarding copyright
+ * ownership. You may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Copyright (C) 2021 Kefu Chai <
tcha...@gmail.com>
+ */
+
+#pragma once
+
+#include <boost/intrusive/list.hpp>
+#include <seastar/core/future.hh>
+#include <seastar/core/reactor.hh>
+#include <seastar/core/sharded.hh>
+#include <memory>
+
+struct spdk_thread;
+
+namespace bi = boost::intrusive;
+
+namespace seastar::spdk {
+
+class thread_entry {
+ bi::list_member_hook<> _hook;
+public:
+ using container_list_t = bi::list<thread_entry,
+ bi::member_hook<thread_entry, bi::list_member_hook<>, &thread_entry::_hook>>;
+ spdk_thread* thread() noexcept;
+ static thread_entry* from_thread(spdk_thread* thread);
+};
+
+namespace internal {
+
+class thread_msg {
+public:
+ virtual void run_and_dispose() noexcept = 0;
+ seastar::future<> get_future() {
+ return _pr.get_future();
+ }
+protected:
+ seastar::promise<> _pr;
+ ~thread_msg() = default;
+};
+
+template <typename Func>
+class lambda_thread_msg final : public thread_msg {
+ Func _func;
+public:
+ lambda_thread_msg(Func&& func) : _func(std::move(func)) {}
+ void run_and_dispose() noexcept final {
+ std::move(_func)();
+ _pr.set_value();
+ delete this;
+ }
+};
+}
+
+class executor : public peering_sharded_service<executor> {
+ using sharded_executor_t = sharded<executor>;
+public:
+ future<> start();
+ future<> stop();
+ bool poll();
+ void schedule_thread(spdk_thread* thread);
+
+ template <typename Func>
+ static future<> send_to(spdk_thread *thread, Func&& func) noexcept {
+ auto msg = new internal::lambda_thread_msg<Func>(std::move(func));
+ return do_send_to(thread, msg);
+ }
+ static sharded_executor_t& instance();
+
+private:
+ static future<> do_send_to(spdk_thread* thread, internal::thread_msg* msg);
+ std::unique_ptr<reactor::poller> poller;
+ thread_entry::container_list_t _threads;
+ uint64_t _tsc_last;
+ static sharded_executor_t* s_executor;
+};
+
+struct run_with_spdk_thread {
+ run_with_spdk_thread(spdk_thread* thread);
+ ~run_with_spdk_thread();
+};
+
+}
diff --git a/src/core/app-template.cc b/src/core/app-template.cc
index e6698662..22e74262 100644
--- a/src/core/app-template.cc
+++ b/src/core/app-template.cc
@@ -23,6 +23,7 @@
#include <seastar/core/reactor.hh>
#include <seastar/core/alien.hh>
#include <seastar/core/scollectd.hh>
+#include <seastar/core/spdk_app.hh>
#include <seastar/core/metrics_api.hh>
#include <boost/program_options.hpp>
#include <seastar/core/print.hh>
@@ -67,7 +68,9 @@ app_template::app_template(app_template::config cfg)
_opts_conf_file.add(smp::get_options_description());
_opts_conf_file.add(scollectd::get_options_description());
_opts_conf_file.add(log_cli::get_options_description());
-
+#ifdef SEASTAR_HAVE_SPDK
+ _opts_conf_file.add(spdk::app::get_options_description());
+#endif
_opts.add(_opts_conf_file);
}
diff --git a/src/core/reactor.cc b/src/core/reactor.cc
index 97d557c5..374b9055 100644
--- a/src/core/reactor.cc
+++ b/src/core/reactor.cc
@@ -93,6 +93,9 @@
#include <seastar/core/dpdk_rte.hh>
#include <rte_lcore.h>
#include <rte_launch.h>
+#elif defined(SEASTAR_HAVE_SPDK)
+#include <seastar/core/spdk_app.hh>
+#include <spdk/env.h>
#endif
#include <seastar/core/prefetch.hh>
#include <exception>
@@ -3669,6 +3672,11 @@ void smp::allocate_reactor(unsigned id, reactor_backend_selector rbs, reactor_co
void smp::cleanup() noexcept {
smp::_threads = std::vector<posix_thread>();
_thread_loops.clear();
+#ifdef SEASTAR_HAVE_SPDK
+ if (_using_spdk) {
+ spdk::env::stop();
+ }
+#endif
}
void smp::cleanup_cpu() {
@@ -3927,6 +3935,8 @@ void smp::configure(boost::program_options::variables_map configuration, reactor
#ifdef SEASTAR_HAVE_DPDK
_using_dpdk = configuration.count("dpdk-pmd");
+#elif defined(SEASTAR_HAVE_SPDK)
+ _using_spdk = configuration.count("spdk-pmd");
#endif
auto thread_affinity = configuration["thread-affinity"].as<bool>();
if (configuration.count("overprovisioned")
@@ -3935,6 +3945,8 @@ void smp::configure(boost::program_options::variables_map configuration, reactor
}
if (!thread_affinity && _using_dpdk) {
fmt::print("warning: --thread-affinity 0 ignored in dpdk mode\n");
+ } else if (!thread_affinity && _using_spdk) {
+ fmt::print("warning: --thread-affinity 0 ignored in spdk mode\n");
}
auto mbind = configuration["mbind"].as<bool>();
if (!thread_affinity) {
@@ -4080,6 +4092,15 @@ void smp::configure(boost::program_options::variables_map configuration, reactor
}
dpdk::eal::init(cpus, configuration);
}
+#elif defined(SEASTAR_HAVE_SPDK)
+ if (_using_spdk) {
+ try {
+ spdk::env::start(allocations, configuration);
+ } catch (const std::exception& e) {
+ seastar_logger.error(e.what());
+ _exit(1);
+ }
+ }
#endif
// Better to put it into the smp class, but at smp construction time
diff --git a/src/core/spdk_app.cc b/src/core/spdk_app.cc
new file mode 100644
index 00000000..f1a21d7a
--- /dev/null
+++ b/src/core/spdk_app.cc
@@ -0,0 +1,310 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:4; indent-tabs-mode:nil -*-
+/*
+ * This file is open source software, licensed to you under the terms
+ * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
+ * distributed with this work for additional information regarding copyright
+ * ownership. You may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Copyright (C) 2021 Kefu Chai <
tcha...@gmail.com>
+ */
+
+#include <seastar/core/spdk_app.hh>
+#include <seastar/util/defer.hh>
+#include <seastar/core/thread.hh>
+#include <spdk/cpuset.h>
+#include <spdk/env.h>
+#include <spdk/init.h>
+#include <spdk/log.h>
+#include <spdk/string.h>
+#include <spdk/thread.h>
+
+namespace seastar::spdk {
+
+seastar::logger logger("spdk");
+
+namespace env {
+
+void start(const std::vector<resource::cpu>& cpuset,
+ const boost::program_options::variables_map& opts)
+{
+
logger.info("env starting");
+ spdk_env_opts env_opts = {};
+ spdk_env_opts_init(&env_opts);
+
+ std::string core_list;
+ for (auto& cpu : cpuset) {
+ if (!core_list.empty()) {
+ core_list.append(",");
+ }
+ core_list.append(std::to_string(cpu.cpu_id));
+ }
+ core_list = fmt::format("[{}]", core_list);
+ env_opts.core_mask = core_list.c_str();
+
+ if (opts.count("main-core")) {
+ env_opts.main_core = std::stoi(opts["main-core"].as<std::string>());
+ }
+ if (opts.count("mem-size")) {
+ const std::string mem_size_str = opts["mem-size"].as<std::string>();
+ uint64_t mem_size_mb;
+ bool mem_size_has_prefix;
+ if (spdk_parse_capacity(mem_size_str.c_str(),
+ &mem_size_mb,
+ &mem_size_has_prefix) != 0) {
+ throw std::invalid_argument(
+ fmt::format("invalid memory pool size `--mem-size {}`",
+ mem_size_str));
+ }
+ if (mem_size_has_prefix) {
+ // convert mem size to MiB
+ mem_size_mb >>= 20;
+ }
+ if (mem_size_mb > std::numeric_limits<int>::max()) {
+ throw std::invalid_argument(
+ fmt::format("memory pool size too large `--mem-size {}`",
+ mem_size_mb));
+ }
+ env_opts.mem_size = static_cast<int>(mem_size_mb);
+ }
+ if (opts.count("no-pci")) {
+ env_opts.no_pci = true;
+ }
+ if (opts.count("single-file-segments")) {
+ env_opts.hugepage_single_segments = true;
+ }
+ if (opts.count("huge-unlink")) {
+ env_opts.unlink_hugepage = true;
+ }
+ std::string hugedir;
+ if (opts.count("hugepages")) {
+ hugedir = opts["hugepages"].as<std::string>();
+ env_opts.hugedir = hugedir.c_str();
+ }
+ std::vector<spdk_pci_addr> pci_addrs;
+ if (opts.count("pci-blocked") && opts.count("pci-allowed")) {
+ throw std::invalid_argument("--pci-blocked and --pci-allowed cannot be used at the same time");
+ } else if (opts.count("pci-blocked")) {
+ for (const auto& bdf : opts["pci-blocked"].as<std::vector<std::string>>()) {
+ spdk_pci_addr pci_addr;
+ spdk_pci_addr_parse(&pci_addr, bdf.c_str());
+ pci_addrs.push_back(pci_addr);
+ }
+ env_opts.pci_blocked = &pci_addrs[0];
+ env_opts.num_pci_addr = pci_addrs.size();
+ } else if (opts.count("pci-allowed")) {
+ for (const auto& bdf : opts["pci-allowed"].as<std::vector<std::string>>()) {
+ spdk_pci_addr pci_addr;
+ spdk_pci_addr_parse(&pci_addr, bdf.c_str());
+ pci_addrs.push_back(pci_addr);
+ }
+ env_opts.pci_allowed = &pci_addrs[0];
+ env_opts.num_pci_addr = pci_addrs.size();
+ }
+ std::string iova_mode;
+ if (opts.count("iova-mode")) {
+ iova_mode = opts["iova-mode"].as<std::string>();
+ env_opts.iova_mode = iova_mode.c_str();
+ }
+ if (spdk_env_init(&env_opts) < 0) {
+ throw std::runtime_error("unable to initialize SPDK env");
+ }
+
logger.info("env starting: done");
+}
+
+void stop() noexcept
+{
+
logger.info("env stopping");
+ spdk_env_fini();
+}
+}
+}
+
+namespace {
+class subsystem_init_desc {
+ seastar::promise<> _pr;
+public:
+ void complete_with(int rc) {
+ seastar::spdk::
logger.info("subsystem initialized: {}", rc);
+ if (rc) {
+ _pr.set_exception(std::runtime_error("unable to init SPDK subsystem"));
+ } else {
+ _pr.set_value();
+ }
+ delete this;
+ }
+ seastar::future<> get_future() {
+ return _pr.get_future();
+ }
+};
+
+class msg_desc {
+ seastar::promise<> _pr;
+public:
+ void complete() {
+ _pr.set_value();
+ delete this;
+ }
+ seastar::future<> get_future() {
+ return _pr.get_future();
+ }
+};
+
+constexpr seastar::log_level spdk_log_to_seastar_level(int level)
+{
+ switch (level) {
+ case SPDK_LOG_DISABLED:
+ return seastar::log_level(static_cast<int>(seastar::log_level::trace) + 1);
+ case SPDK_LOG_ERROR:
+ return seastar::log_level::error;
+ case SPDK_LOG_WARN:
+ return seastar::log_level::warn;
+ case SPDK_LOG_NOTICE:
+ return seastar::log_level::info;
+ case SPDK_LOG_INFO:
+ return seastar::log_level::debug;
+ case SPDK_LOG_DEBUG:
+ return seastar::log_level::trace;
+ default:
+ return seastar::log_level::info;
+ }
+}
+
+void spdk_do_log(int level, const char *file, const int line,
+ const char *func, const char *format, va_list args)
+{
+ static const int MAX_TMPBUF = 1024;
+ char buf[MAX_TMPBUF];
+ int len = vsnprintf(buf, sizeof(buf), format, args);
+ if (len > 0 && buf[len - 1] == '\n') {
+ // remove the trailing newline, as seastar always add it for us
+ buf[len - 1] = '\0';
+ }
+ seastar::spdk::logger.log(spdk_log_to_seastar_level(level),
+ "{}:{:4d}:{}: {}",
+ file, line, func, buf);
+}
+
+}
+
+namespace seastar::spdk {
+
+future<> app::run(const boost::program_options::variables_map& opts,
+ std::function<future<> ()>&& func) noexcept
+{
+ spdk_log_open(spdk_do_log);
+
+ return seastar::async([opts, func = std::move(func), this] {
+ sharded_executor.start().then([this] {
+ return sharded_executor.invoke_on_all(&executor::start);
+ }).get();
+ auto stop_executor = seastar::defer([&] () noexcept {
+ sharded_executor.stop().get();
+ });
+ assert(app_thread == nullptr);
+ spdk_cpuset cpu_mask = {};
+ spdk_cpuset_set_cpu(&cpu_mask, spdk_env_get_current_core(), true);
+ app_thread = spdk_thread_create("app_thread", &cpu_mask);
+ if (app_thread == nullptr) {
+ throw std::bad_alloc();
+ }
+ run_with_spdk_thread run_with(app_thread);
+ start(opts).get();
+ auto stop_me = seastar::defer([&] () noexcept {
+ stop().get();
+ });
+
+ futurize_invoke(func).get();
+ });
+}
+
+static void spdk_subsystem_init_cpl(int rc, void* arg)
+{
+ auto* desc = static_cast<subsystem_init_desc*>(arg);
+ desc->complete_with(rc);
+}
+
+future<> app::start(const boost::program_options::variables_map& opts)
+{
+
logger.info("app start");
+ // ensure that start() is able to find app_thread using spdk_get_thread(),
+ // the underlying SPDK functions need to hook poolers to "this" thread.
+ auto init_desc = std::make_unique<subsystem_init_desc>();
+ auto init_done = init_desc->get_future();
+ auto rpc_addr = opts["spdk-rpc-socket"].as<std::string>();
+ if (opts.count("spdk-config")) {
+
+ auto spdk_config = opts["spdk-config"].as<std::string>();
+ spdk_subsystem_init_from_json_config(
+ spdk_config.c_str(),
+ rpc_addr.c_str(),
+ spdk_subsystem_init_cpl,
+ init_desc.release(),
+ opts.count("spdk-json-ignore-init-errors"));
+ } else {
+ spdk_subsystem_init(
+ spdk_subsystem_init_cpl,
+ init_desc.release());
+ }
+ return init_done.then([rpc_addr] {
+ if (int rc = spdk_rpc_initialize(rpc_addr.c_str()); rc != 0) {
+ throw std::runtime_error("unable to init SPDK RPC");
+ }
+ });
+}
+
+// seastar takes care of the cleanup, so just use a dummy callback here
+static void spdk_subsystem_fini_cpl(void* arg)
+{
+ auto *desc = static_cast<msg_desc*>(arg);
+ desc->complete();
+}
+
+future<> app::stop()
+{
+
logger.info("app stopping");
+ spdk_rpc_finish();
+ auto fini_desc = std::make_unique<msg_desc>();
+ auto fini_done = fini_desc->get_future();
+ spdk_subsystem_fini(spdk_subsystem_fini_cpl, fini_desc.release());
+ return fini_done;
+}
+
+boost::program_options::options_description app::get_options_description()
+{
+ namespace bpo = boost::program_options;
+ bpo::options_description opts("SPDK options");
+ opts.add_options()
+ ("spdk-pmd", "Use SPDK PMD drivers")
+ ("spdk-rpc-socket",
+ bpo::value<std::string>()->default_value(SPDK_DEFAULT_RPC_ADDR),
+ "RPC listen address")
+ ("spdk-config", bpo::value<std::string>(), "JSON config file")
+ ("spdk-json-ignore-init-errors", "don't exit on invalid config entry")
+ ("iova-mode", bpo::value<std::string>(),
+ "set IOVA mode ('pa' for IOVA_PA and 'va' for IOVA_VA)")
+ ("huge-unlink", "unlink huge files after initialization")
+ ("mem-size", bpo::value<std::string>(),
+ "memory size in MB for DPDK")
+ ("no-pci", "disable PCI access")
+ ("single-file-segments", "force creating just one hugetlbfs file")
+ ("pci-blocked", bpo::value<std::vector<std::string>>()->multitoken(),
+ "pci addr to block (can be used more than once)")
+ ("pci-allowed", bpo::value<std::vector<std::string>>()->multitoken(),
+ "pci addr to allow (--pci-blocked and --pci-allowed cannot be used at the same time)")
+ ;
+ return opts;
+}
+
+}
diff --git a/src/core/spdk_bdev.cc b/src/core/spdk_bdev.cc
new file mode 100644
index 00000000..9c3828c3
--- /dev/null
+++ b/src/core/spdk_bdev.cc
@@ -0,0 +1,158 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:4; indent-tabs-mode:nil -*-
+/*
+ * This file is open source software, licensed to you under the terms
+ * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
+ * distributed with this work for additional information regarding copyright
+ * ownership. You may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Copyright (C) 2021 Kefu Chai <
tcha...@gmail.com>
+ */
+
+#include <seastar/core/spdk_bdev.hh>
+#include <seastar/util/log.hh>
+#include <spdk/bdev.h>
+#include <spdk/string.h>
+#include <spdk/thread.h>
+#include <memory>
+
+namespace {
+
+class io_completion_desc {
+ seastar::promise<> _pr;
+public:
+ void complete_with(struct spdk_bdev_io* bdev_io, bool success) {
+ if (success) {
+ _pr.set_value();
+ } else {
+ _pr.set_exception(
+ std::system_error(ECONNABORTED,
+ std::system_category(),
+ "bdev IO error"));
+ }
+ if (bdev_io != nullptr) {
+ spdk_bdev_free_io(bdev_io);
+ }
+ delete this;
+ }
+ template <class E>
+ void fail_with(E&& e) {
+ _pr.set_exception(std::make_exception_ptr(std::move(e)));
+ }
+ seastar::future<> get_future() {
+ return _pr.get_future();
+ }
+};
+
+}
+
+namespace seastar::spdk {
+
+extern logger logger;
+
+block_device::~block_device()
+{
+ if (io_channel) {
+ spdk_put_io_channel(io_channel);
+ }
+ if (desc) {
+ spdk_bdev_close(desc);
+ }
+}
+
+std::unique_ptr<block_device> block_device::open(const std::string& bdev_name)
+{
+ std::unique_ptr<block_device> bdev{new block_device};
+ int rc = spdk_bdev_open_ext(bdev_name.c_str(),
+ true,
+ reinterpret_cast<spdk_bdev_event_cb_t>(event_cb),
+ bdev.get(),
+ &bdev->desc);
+ if (rc) {
+ logger.error("unable to open bdev {}: {}",
+ bdev_name, spdk_strerror(-rc));
+ throw std::runtime_error(fmt::format("unable to open bdev {}", bdev_name));
+ }
+ bdev->bdev = spdk_bdev_desc_get_bdev(bdev->desc);
+ bdev->io_channel = spdk_bdev_get_io_channel(bdev->desc);
+ if (bdev->io_channel == nullptr) {
+ logger.error("unable to open bdev I/O channel");
+ throw std::runtime_error(fmt::format("unable to open io channel"));
+ }
+ return bdev;
+}
+
+void block_device::event_cb(int type, spdk_bdev* bdev, void* event_ctx)
+{}
+
+static void spdk_bdev_io_cpl(spdk_bdev_io* bdev_io, bool success, void* arg)
+{
+
logger.info("io done");
+ auto* desc = static_cast<io_completion_desc*>(arg);
+ desc->complete_with(bdev_io, success);
+}
+
+future<> block_device::write(uint64_t pos, const void* buffer, size_t len)
+{
+ assert(bdev);
+
logger.info("write({}, {})", pos, len);
+ auto io_desc = std::make_unique<io_completion_desc>();
+ auto io_done = io_desc->get_future();
+ int rc = spdk_bdev_write(desc, io_channel,
+ const_cast<void*>(buffer), pos, len,
+ spdk_bdev_io_cpl, io_desc.release());
+ if (rc == 0) {
+ return io_done;
+ }
+ if (rc == -ENOMEM) {
+ io_desc->fail_with(std::bad_alloc());
+ } else {
+ // -EBADF or -EINVAL
+ io_desc->fail_with(std::invalid_argument("out of range"));
+ }
+ return io_done;
+}
+
+future<> block_device::read(uint64_t pos, void* buffer, size_t len)
+{
+ assert(bdev);
+ auto io_desc = std::make_unique<io_completion_desc>();
+ auto io_done = io_desc->get_future();
+ int rc = spdk_bdev_read(desc, io_channel, buffer, pos, len,
+ spdk_bdev_io_cpl, io_desc.release());
+ if (rc == 0) {
+ return io_done;
+ }
+ if (rc == -ENOMEM) {
+ io_desc->fail_with(std::bad_alloc());
+ } else {
+ // --EINVAL
+ io_desc->fail_with(std::invalid_argument("out of range"));
+ }
+ return io_done;
+}
+
+uint32_t block_device::block_size() const
+{
+ assert(bdev);
+ return spdk_bdev_get_block_size(bdev);
+}
+
+size_t block_device::memory_dma_alignment() const
+{
+ assert(bdev);
+ return spdk_bdev_get_buf_align(bdev);
+}
+
+}
diff --git a/src/core/spdk_lib.cc b/src/core/spdk_lib.cc
new file mode 100644
index 00000000..f21899fb
--- /dev/null
+++ b/src/core/spdk_lib.cc
@@ -0,0 +1,39 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:4; indent-tabs-mode:nil -*-
+/*
+ * This file is open source software, licensed to you under the terms
+ * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
+ * distributed with this work for additional information regarding copyright
+ * ownership. You may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Copyright (C) 2021 Kefu Chai <
tcha...@gmail.com>
+ */
+
+#include <seastar/core/spdk_lib.hh>
+#include <spdk/env.h>
+
+namespace seastar::spdk {
+
+temporary_buffer<char> dma_zmalloc(size_t size, size_t align)
+{
+ void* buf = spdk_dma_zmalloc_socket(size, align, nullptr, SPDK_ENV_SOCKET_ID_ANY);
+ if (!buf) {
+ throw std::bad_alloc();
+ }
+ return {static_cast<char*>(buf), size, seastar::make_deleter([buf] {
+ spdk_dma_free(buf);
+ })};
+}
+
+}
diff --git a/src/core/spdk_thread.cc b/src/core/spdk_thread.cc
new file mode 100644
index 00000000..07b8dc4e
--- /dev/null
+++ b/src/core/spdk_thread.cc
@@ -0,0 +1,158 @@
+// -*- mode:C++; tab-width:8; c-basic-offset:4; indent-tabs-mode:nil -*-
+/*
+ * This file is open source software, licensed to you under the terms
+ * of the Apache License, Version 2.0 (the "License"). See the NOTICE file
+ * distributed with this work for additional information regarding copyright
+ * ownership. You may not use this file except in compliance with the License.
+ *
+ * You may obtain a copy of the License at
+ *
+ *
http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+/*
+ * Copyright (C) 2021 Kefu Chai <
tcha...@gmail.com>
+ */
+
+#include <seastar/util/log.hh>
+#include <seastar/core/spdk_thread.hh>
+#include <spdk/thread.h>
+
+namespace seastar::spdk {
+
+extern logger logger;
+
+spdk_thread* thread_entry::thread() noexcept
+{
+ return spdk_thread_get_from_ctx(reinterpret_cast<void*>(this));
+}
+
+thread_entry* thread_entry::from_thread(spdk_thread* thread)
+{
+ return static_cast<thread_entry*>(spdk_thread_get_ctx(thread));
+}
+
+static int thread_do_op(spdk_thread* thread, spdk_thread_op op)
+{
+ switch (op) {
+ case SPDK_THREAD_OP_NEW: {
+ spdk_cpuset* cpumask = spdk_thread_get_cpumask(thread);
+ unsigned shard = 0;
+ while (shard < smp::count) {
+ if (spdk_cpuset_get_cpu(cpumask, shard)) {
+ break;
+ }
+ }
+ if (shard == smp::count) {
+ logger.error("unable to find executor for new thread");
+ return -1;
+ }
+ // FIXME: future is discarded
+ (void)executor::instance().invoke_on(
+ shard, [thread] (executor& group) {
+ group.schedule_thread(thread);
+ });
+ return 0;
+ }
+ case SPDK_THREAD_OP_RESCHED:
+ return -ENOTSUP;
+ default:
+ return -ENOTSUP;
+ }
+}
+
+static bool thread_op_supported(spdk_thread_op op)
+{
+ switch (op) {
+ case SPDK_THREAD_OP_NEW:
+ return true;
+ case SPDK_THREAD_OP_RESCHED:
+ return false;
+ default:
+ return false;
+ }
+}
+
+future<> executor::start()
+{
+
logger.info("executor#{} start", seastar::this_shard_id());
+ poller = std::make_unique<reactor::poller>(reactor::poller::simple([this] {
+ return poll();
+ }));
+ if (seastar::this_shard_id() == 0) {
+ spdk_thread_lib_init_ext(thread_do_op, thread_op_supported,
+ sizeof(thread_entry));
+ sharded_executor_t& instance = container();
+ s_executor = &instance;
+ }
+ return make_ready_future<>();
+}
+
+future<> executor::stop()
+{
+ if (seastar::this_shard_id() == 0) {
+ s_executor = nullptr;
+ spdk_thread_lib_fini();
+ }
+ poller.reset();
+ return make_ready_future<>();
+}
+
+bool executor::poll()
+{
+ int nr = 0;
+ for (auto& entry : _threads) {
+ spdk_thread *thread = entry.thread();
+ nr += spdk_thread_poll(thread, 0, _tsc_last);
+ _tsc_last = spdk_thread_get_last_tsc(thread);
+ if (__builtin_expect(spdk_thread_is_exited(thread) &&
+ spdk_thread_is_idle(thread), false)) {
+ _threads.erase(thread_entry::container_list_t::s_iterator_to(entry));
+ spdk_thread_destroy(thread);
+ }
+ }
+ logger.trace("poll(): {}", nr);
+ return nr > 0;
+}
+
+void executor::schedule_thread(spdk_thread* thread)
+{
+ _threads.push_back(*thread_entry::from_thread(thread));
+}
+
+executor::sharded_executor_t& executor::instance()
+{
+ assert(s_executor);
+ return *s_executor;
+}
+
+static void spdk_msg_call(void* ctx)
+{
+ auto* task = static_cast<internal::thread_msg*>(ctx);
+ task->run_and_dispose();
+}
+
+future<> executor::do_send_to(spdk_thread* thread,
+ internal::thread_msg* msg)
+{
+ spdk_thread_send_msg(thread, spdk_msg_call, msg);
+ return msg->get_future();
+}
+
+executor::sharded_executor_t* executor::s_executor = nullptr;
+
+run_with_spdk_thread::run_with_spdk_thread(spdk_thread* thread) {
+ spdk_set_thread(thread);
+}
+
+run_with_spdk_thread::~run_with_spdk_thread() {
+ spdk_set_thread(nullptr);
+}
+
+}
--
2.33.0