Skip to content

Commit 9694d2b

Browse files
authored
add wrapper of spdk nvme interfaces for local nvme ssd (alibaba#845)
* add wrapper of spdk nvme interfaces for local nvme ssd * fix:(1)remove qpair_manager's unique_ptr, but still need the if branch to avoid add fini_hook repetitive; (2)replace sum with empty check in next_sge_fn; (3)move function typedef to one line; (4)add to iovector (base class of IOVectorEntity) * optimze implementation of iovector::assign with memcpy
1 parent d30ea67 commit 9694d2b

13 files changed

Lines changed: 577 additions & 22 deletions

CMakeLists.txt

Lines changed: 8 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ because Photon will register coroutine locks for crypto. But don't bother if you
2828
option(PHOTON_ENABLE_SASL "enable sasl" OFF)
2929
option(PHOTON_ENABLE_MIMIC_VDSO "enable mimic vdso" OFF)
3030
option(PHOTON_ENABLE_FSTACK_DPDK "Use f-stack + DPDK as the event engine" OFF)
31-
option(PHOTON_ENABLE_SPDK_BDEV "Use SPDK bdev concept" OFF)
31+
option(PHOTON_ENABLE_SPDK "Use SPDK bdev && nvme concept" OFF)
3232
option(PHOTON_ENABLE_EXTFS "enable extfs" OFF)
3333
option(PHOTON_ENABLE_ECOSYSTEM "enable ecosystem" OFF)
3434
option(PHOTON_ENABLE_RSOCKET "enable rsocket" OFF)
@@ -146,7 +146,7 @@ endif ()
146146
if (PHOTON_ENABLE_FSTACK_DPDK)
147147
LIST(APPEND dependencies fstack)
148148
endif ()
149-
if (PHOTON_ENABLE_SPDK_BDEV)
149+
if (PHOTON_ENABLE_SPDK)
150150
LIST(APPEND dependencies spdk)
151151
endif()
152152
if (PHOTON_ENABLE_EXTFS)
@@ -215,9 +215,9 @@ file(GLOB PHOTON_SRC RELATIVE "${PROJECT_SOURCE_DIR}"
215215
if (APPLE)
216216
list(APPEND PHOTON_SRC io/kqueue.cpp)
217217
else ()
218-
file(GLOB CACHE_SRC
218+
file(GLOB CACHE_SRC
219219
fs/cache/*.cpp
220-
fs/cache/full_file_cache/*.cpp
220+
fs/cache/full_file_cache/*.cpp
221221
fs/cache/persistent_cache/*.cpp
222222
)
223223
list(APPEND PHOTON_SRC
@@ -242,8 +242,8 @@ endif ()
242242
if (PHOTON_ENABLE_FSTACK_DPDK)
243243
list(APPEND PHOTON_SRC io/fstack-dpdk.cpp)
244244
endif ()
245-
if (PHOTON_ENABLE_SPDK_BDEV)
246-
list(APPEND PHOTON_SRC io/spdkbdev-wrapper.cpp)
245+
if (PHOTON_ENABLE_SPDK)
246+
list(APPEND PHOTON_SRC io/spdkbdev-wrapper.cpp io/spdknvme-wrapper.cpp)
247247
endif ()
248248
if (PHOTON_ENABLE_EXTFS)
249249
file(GLOB EXTFS_SRC fs/extfs/*.cpp)
@@ -297,8 +297,7 @@ if (PHOTON_ENABLE_FSTACK_DPDK)
297297
target_compile_definitions(photon_obj PRIVATE ENABLE_FSTACK_DPDK)
298298
target_include_directories(photon_obj PRIVATE ${FSTACK_INCLUDE_DIRS})
299299
endif()
300-
if (PHOTON_ENABLE_SPDK_BDEV)
301-
target_compile_definitions(photon_obj PRIVATE ENABLE_SPDK_BDEV)
300+
if (PHOTON_ENABLE_SPDK)
302301
target_include_directories(photon_obj PRIVATE ${SPDK_INCLUDE_DIRS})
303302
endif()
304303
if (PHOTON_ENABLE_EXTFS)
@@ -345,7 +344,7 @@ endif ()
345344
if (PHOTON_ENABLE_FSTACK_DPDK)
346345
list(APPEND static_deps ${FSTACK_LIBRARIES})
347346
endif ()
348-
if (PHOTON_ENABLE_SPDK_BDEV)
347+
if (PHOTON_ENABLE_SPDK)
349348
list(APPEND static_deps ${SPDK_LIBRARIES})
350349
endif ()
351350
if (PHOTON_ENABLE_EXTFS)

common/iovector.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -399,6 +399,14 @@ class iovector
399399
return v.iov_len + push_back_more(bytes - v.iov_len);
400400
}
401401

402+
void assign(struct iovec* iovs, uint16_t iovcnt) {
403+
assert(iovcnt >= 0);
404+
assert(iovcnt + iov_begin < capacity);
405+
clear();
406+
memcpy(begin(), iovs, iovcnt * sizeof(*iovs));
407+
iov_end += iovcnt;
408+
}
409+
402410
// pop an struct iovec element, and
403411
// return the # of bytes actually popped
404412
size_t pop_front()

examples/CMakeLists.txt

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,9 @@ if (PHOTON_ENABLE_FSTACK_DPDK)
3535
target_link_libraries(fstack-dpdk-demo PRIVATE ${FSTACK_LIBRARIES} photon_static)
3636
endif ()
3737

38-
if (PHOTON_ENABLE_SPDK_BDEV)
39-
add_executable(bdev-example spdk-bdev/read_write.cpp)
38+
if (PHOTON_ENABLE_SPDK)
39+
add_executable(bdev-example spdk/bdev_read_write.cpp)
4040
target_link_libraries(bdev-example PRIVATE ${SPDK_LIBRARIES} photon_static)
41+
add_executable(nvme-example spdk/nvme_read_write.cpp)
42+
target_link_libraries(nvme-example PRIVATE ${SPDK_LIBRARIES} photon_static)
4143
endif ()
Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -5,19 +5,21 @@
55
- ./configure --with-shared
66
- make -j 8
77

8-
(2) compile photon
9-
photon spdk bdev wrapper rely on some spdk dynamic libraries, and these libraries rely on some dpdk libraries and the isal library.
10-
- please change the ${SPDK_ROOT} in CMake/Findspdk.cmake to yours
8+
(2) compile photon
9+
photon spdk bdev wrapper rely on some spdk dynamic libraries, and these libraries rely on some dpdk libraries and the isal library.
10+
- please change the ${SPDK_ROOT} in CMake/Findspdk.cmake to yours
1111
- cd /path/to/photon
12-
- cmake -B build -DPHOTON_ENABLE_SPDK_BDEV=ON -DPHOTON_BUILD_TESTING=ON
12+
- cmake -B build -DPHOTON_ENABLE_SPDK=ON -DPHOTON_BUILD_TESTING=ON
1313
- make -C build -j 8
1414

1515
# how to run
16-
example
16+
example
1717
``` shell
18-
sudo ./build/examples-output/bdev-example --json ./examples/spdk-bdev/bdev.json
18+
sudo ./build/examples-output/bdev-example --json ./examples/spdk/bdev.json
19+
sudo ./build/examples-output/nvme-example
1920
```
20-
tests
21+
tests
2122
``` shell
2223
sudo ./build/output/test-spdkbdev
24+
sudo ./build/output/test-spdknvme
2325
```

examples/spdk/nvme_read_write.cpp

Lines changed: 64 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
#include <photon/io/spdknvme-wrapper.h>
2+
#include <photon/common/alog-stdstring.h>
3+
#include <photon/common/iovector.h>
4+
5+
int main() {
6+
char trid_str[] = "trtype:pcie traddr:0000:86:00.0";
7+
uint32_t nsid = 1;
8+
9+
if (photon::spdk::nvme_env_init() != 0) {
10+
LOG_ERROR_RETURN(0, -1, "nvme_env_init failed");
11+
}
12+
DEFER(photon::spdk::nvme_env_fini());
13+
14+
struct spdk_nvme_ctrlr* ctrlr = photon::spdk::nvme_probe_attach(trid_str);
15+
if (ctrlr == nullptr) {
16+
LOG_ERROR_RETURN(0, -1, "nvme_probe_attach failed");
17+
}
18+
DEFER(photon::spdk::nvme_detach(ctrlr));
19+
20+
struct spdk_nvme_ns* ns = photon::spdk::nvme_get_namespace(ctrlr, nsid);
21+
if (ns == nullptr) {
22+
LOG_ERROR_RETURN(0, -1, "nvme_get_namespace failed");
23+
}
24+
25+
if (photon::init() != 0) {
26+
LOG_ERROR_RETURN(0, -1, "photon::init failed");
27+
}
28+
DEFER(photon::fini());
29+
30+
LOG_INFO("alloc io qpair");
31+
struct spdk_nvme_qpair* qpair = photon::spdk::nvme_ctrlr_alloc_io_qpair(ctrlr, nullptr, 0);
32+
if (qpair == nullptr) {
33+
LOG_ERROR_RETURN(0, -1, "nvme_ctrlr_alloc_io_qpair failed");
34+
}
35+
DEFER(photon::spdk::nvme_ctrlr_free_io_qpair(ctrlr, qpair));
36+
37+
uint32_t sectorsz = spdk_nvme_ns_get_sector_size(ns);
38+
uint32_t nsec = 4;
39+
uint64_t bufsz = sectorsz * nsec;
40+
41+
void* buf_write = spdk_zmalloc(bufsz, 0, nullptr, SPDK_ENV_SOCKET_ID_ANY, 1);
42+
void* buf_read = spdk_zmalloc(bufsz, 0, nullptr, SPDK_ENV_SOCKET_ID_ANY, 1);
43+
if (buf_write == nullptr || buf_read == nullptr) {
44+
LOG_ERROR_RETURN(0, -1, "spdk_zmalloc failed");
45+
}
46+
DEFER(spdk_free(buf_write));
47+
DEFER(spdk_free(buf_read));
48+
49+
// prepare datas to write
50+
char test_data[] = "hello world";
51+
strncpy((char*)buf_write, test_data, 12);
52+
53+
LOG_INFO("write");
54+
if (photon::spdk::nvme_ns_cmd_write(ns, qpair, buf_write, 0, nsec, 0) != 0) {
55+
LOG_ERROR_RETURN(0, -1, "nvme_ns_cmd_write failed");
56+
}
57+
LOG_INFO("read");
58+
if (photon::spdk::nvme_ns_cmd_read(ns, qpair, buf_read, 0, nsec, 0) != 0) {
59+
LOG_ERROR_RETURN(0, -1, "nvme_ns_cmd_read failed");
60+
}
61+
62+
// print
63+
LOG_INFO("burwrite=`, bufread=`", (char*)buf_write, (char*)buf_read);
64+
}
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
../../../io/spdknvme-wrapper.h

io/spdknvme-wrapper.cpp

Lines changed: 209 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,209 @@
1+
#include "spdknvme-wrapper.h"
2+
#include <photon/common/alog-stdstring.h>
3+
#include <photon/common/expirecontainer.h>
4+
#include <photon/thread/thread.h>
5+
#include <photon/thread/thread11.h>
6+
7+
#include <unordered_map>
8+
9+
10+
namespace photon {
11+
namespace spdk {
12+
13+
struct nvme_qpair {
14+
bool is_complete = false;
15+
struct spdk_nvme_qpair* qpair = nullptr;
16+
photon::join_handle* jh = nullptr;
17+
18+
nvme_qpair(struct spdk_nvme_ctrlr* ctrlr, struct spdk_nvme_io_qpair_opts* opts, size_t opts_size) {
19+
assert(ctrlr != nullptr);
20+
LOG_DEBUG("nvme qpair get into constructor");
21+
qpair = spdk_nvme_ctrlr_alloc_io_qpair(ctrlr, opts, opts_size);
22+
LOG_DEBUG("after spdk alloc io qpair");
23+
jh = thread_enable_join(thread_create11([this]{
24+
while (!is_complete) {
25+
spdk_nvme_qpair_process_completions(qpair, 0);
26+
thread_yield();
27+
}
28+
}));
29+
LOG_DEBUG("nvme qpair get out constructor");
30+
}
31+
32+
~nvme_qpair() {
33+
LOG_DEBUG("nvme qpair get into destructor");
34+
is_complete = true;
35+
LOG_DEBUG("before join");
36+
thread_join(jh);
37+
LOG_DEBUG("after join");
38+
spdk_nvme_ctrlr_free_io_qpair(qpair);
39+
LOG_DEBUG("nvme qpair get out destructor");
40+
}
41+
};
42+
43+
using qpm = ObjectCache<struct spdk_nvme_ctrlr*, struct nvme_qpair*>;
44+
static qpm* get_qpair_manager() {
45+
thread_local qpm* qpair_manager = nullptr;
46+
if (qpair_manager == nullptr) {
47+
qpair_manager = new qpm(3000000);
48+
auto destroy_qpair_manager = []{
49+
if (qpair_manager) {
50+
delete qpair_manager;
51+
}
52+
};
53+
photon::fini_hook(destroy_qpair_manager);
54+
}
55+
return qpair_manager;
56+
}
57+
58+
59+
struct CBContextBase{
60+
int rc = 0;
61+
Awaiter<PhotonContext> awaiter;
62+
static void cb_fn(void *cb_ctx, const struct spdk_nvme_cpl *cpl);
63+
64+
// for vector io
65+
struct iovec *iov; // origin iov
66+
int iovcnt; // origin iovcnt
67+
IOVector iovec;
68+
static void reset_sgl_fn(void *cb_ctx, uint32_t offset);
69+
static int next_sge_fn(void *cb_ctx, void **address, uint32_t *length);
70+
};
71+
void CBContextBase::cb_fn(void *cb_ctx, const struct spdk_nvme_cpl *cpl) {
72+
auto ctx = static_cast<CBContextBase*>(cb_ctx);
73+
if (spdk_nvme_cpl_is_error(cpl)) {
74+
LOG_ERROR("error: `", spdk_nvme_cpl_get_status_string(&cpl->status));
75+
ctx->rc = -1;
76+
}
77+
ctx->awaiter.resume();
78+
}
79+
80+
void CBContextBase::reset_sgl_fn(void *cb_ctx, uint32_t offset) {
81+
LOG_DEBUG("get into reset_sgl_fn", VALUE(offset));
82+
auto ctx = static_cast<CBContextBase*>(cb_ctx);
83+
ctx->iovec.assign(ctx->iov, ctx->iovcnt);
84+
ctx->iovec.extract_front(offset);
85+
}
86+
87+
int CBContextBase::next_sge_fn(void *cb_ctx, void **address, uint32_t *length) {
88+
auto ctx = static_cast<CBContextBase*>(cb_ctx);
89+
if (ctx->iovec.empty()) {
90+
LOG_ERROR_RETURN(0, -1, "ctx->iov_view is empty");
91+
}
92+
*address = ctx->iovec.front().iov_base;
93+
*length = ctx->iovec.front().iov_len;
94+
LOG_DEBUG("get into next_sge_fn", VALUE(*length));
95+
ctx->iovec.pop_front();
96+
return 0;
97+
}
98+
99+
int nvme_env_init() {
100+
struct spdk_env_opts opts;
101+
spdk_env_opts_init(&opts);
102+
opts.name = "photon_spdk_nvme_pcie";
103+
104+
int rc = 0;
105+
if ((rc = spdk_env_init(&opts)) < 0) {
106+
LOG_ERROR_RETURN(0, rc, "spdk_env_init failed");
107+
}
108+
return rc;
109+
}
110+
111+
void nvme_env_fini() {
112+
LOG_DEBUG("get into nvme_env_fini");
113+
spdk_env_fini();
114+
LOG_DEBUG("get out nvme_env_fini");
115+
}
116+
117+
struct spdk_nvme_ctrlr* nvme_probe_attach(const char* trid_str) {
118+
auto probe_cb = [](void *cb_ctx, const struct spdk_nvme_transport_id *trid, struct spdk_nvme_ctrlr_opts *opts) -> bool {
119+
LOG_INFO("probe_cb: do attach in default ", VALUE(trid->traddr));
120+
return true;
121+
};
122+
123+
auto attach_cb = [](void *cb_ctx, const struct spdk_nvme_transport_id *trid, struct spdk_nvme_ctrlr *ctrlr, const struct spdk_nvme_ctrlr_opts *opts) {
124+
LOG_INFO("attach_cb: attach ", VALUE(trid->traddr));
125+
auto ret_ctrlr = (struct spdk_nvme_ctrlr**)cb_ctx;
126+
*ret_ctrlr = ctrlr;
127+
};
128+
129+
struct spdk_nvme_transport_id trid;
130+
spdk_nvme_transport_id_parse(&trid, trid_str);
131+
132+
struct spdk_nvme_ctrlr *ctrlr = nullptr;
133+
spdk_nvme_probe(&trid, &ctrlr, probe_cb, attach_cb, nullptr);
134+
return ctrlr;
135+
}
136+
137+
int nvme_detach(struct spdk_nvme_ctrlr* ctrlr) {
138+
LOG_DEBUG("get into nvme_detach");
139+
spdk_nvme_detach(ctrlr);
140+
LOG_DEBUG("get out nvme_detach");
141+
return 0;
142+
}
143+
144+
struct spdk_nvme_ns* nvme_get_namespace(struct spdk_nvme_ctrlr* ctrlr, uint32_t nsid) {
145+
return spdk_nvme_ctrlr_get_ns(ctrlr, nsid);
146+
}
147+
148+
struct spdk_nvme_qpair* nvme_ctrlr_alloc_io_qpair(struct spdk_nvme_ctrlr* ctrlr, struct spdk_nvme_io_qpair_opts* opts, size_t opts_size) {
149+
LOG_DEBUG("before acquired qpair");
150+
auto m_qpair = get_qpair_manager()->acquire(ctrlr, [&]() -> struct nvme_qpair* {
151+
LOG_DEBUG("before new struct nvme_qpair");
152+
return new nvme_qpair(ctrlr, opts, opts_size);
153+
});
154+
LOG_DEBUG("after acquired qpair");
155+
if (m_qpair != nullptr) {
156+
return m_qpair->qpair;
157+
}
158+
else {
159+
LOG_ERROR_RETURN(0, nullptr, "failed to allocate qpair");
160+
}
161+
}
162+
163+
int nvme_ctrlr_free_io_qpair(struct spdk_nvme_ctrlr* ctrlr, struct spdk_nvme_qpair* qpair) {
164+
LOG_DEBUG("before release qpair");
165+
get_qpair_manager()->release(ctrlr, true);
166+
LOG_DEBUG("after release qpair");
167+
return 0;
168+
}
169+
170+
typedef int (*spdk_nvme_ns_cmd_rw) (struct spdk_nvme_ns *ns, struct spdk_nvme_qpair *qpair, void *payload, uint64_t lba, uint32_t lba_count, spdk_nvme_cmd_cb cb_fn, void *cb_arg, uint32_t io_flags);
171+
172+
typedef int (*spdk_nvme_ns_cmd_rwv) (struct spdk_nvme_ns *ns, struct spdk_nvme_qpair *qpair, uint64_t lba, uint32_t lba_count, spdk_nvme_cmd_cb cb_fn, void *cb_arg, uint32_t io_flags, spdk_nvme_req_reset_sgl_cb reset_sgl_fn, spdk_nvme_req_next_sge_cb next_sge_fn);
173+
174+
static int io_helper(struct spdk_nvme_ns* ns, struct spdk_nvme_qpair* qpair, void* buffer, uint64_t lba, uint32_t lba_count, uint32_t io_flags, spdk_nvme_ns_cmd_rw rw_fn) {
175+
CBContextBase ctx;
176+
int rc = rw_fn(ns, qpair, buffer, lba, lba_count, CBContextBase::cb_fn, &ctx, io_flags);
177+
if (rc != 0) return rc;
178+
ctx.awaiter.suspend();
179+
return ctx.rc;
180+
}
181+
182+
int nvme_ns_cmd_write(struct spdk_nvme_ns* ns, struct spdk_nvme_qpair* qpair, void* buffer, uint64_t lba, uint32_t lba_count, uint32_t io_flags) {
183+
return io_helper(ns, qpair, buffer, lba, lba_count, io_flags, &spdk_nvme_ns_cmd_write);
184+
}
185+
186+
int nvme_ns_cmd_read(struct spdk_nvme_ns* ns, struct spdk_nvme_qpair* qpair, void* buffer, uint64_t lba, uint32_t lba_count, uint32_t io_flags) {
187+
return io_helper(ns, qpair, buffer, lba, lba_count, io_flags, &spdk_nvme_ns_cmd_read);
188+
}
189+
190+
static int vec_io_helper(struct spdk_nvme_ns* ns, struct spdk_nvme_qpair* qpair, struct iovec *iov, int iovcnt, uint64_t lba, uint32_t lba_count, uint32_t io_flags, spdk_nvme_ns_cmd_rwv rwv_fn) {
191+
CBContextBase ctx;
192+
ctx.iov = iov;
193+
ctx.iovcnt = iovcnt;
194+
int rc = rwv_fn(ns, qpair, lba, lba_count, CBContextBase::cb_fn, &ctx, io_flags, CBContextBase::reset_sgl_fn, CBContextBase::next_sge_fn);
195+
if (rc != 0) return rc;
196+
ctx.awaiter.suspend();
197+
return ctx.rc;
198+
}
199+
200+
int nvme_ns_cmd_writev(struct spdk_nvme_ns* ns, struct spdk_nvme_qpair* qpair, struct iovec *iov, int iovcnt, uint64_t lba, uint32_t lba_count, uint32_t io_flags) {
201+
return vec_io_helper(ns, qpair, iov, iovcnt, lba, lba_count, io_flags, &spdk_nvme_ns_cmd_writev);
202+
}
203+
204+
int nvme_ns_cmd_readv(struct spdk_nvme_ns* ns, struct spdk_nvme_qpair* qpair, struct iovec *iov, int iovcnt, uint64_t lba, uint32_t lba_count, uint32_t io_flags) {
205+
return vec_io_helper(ns, qpair, iov, iovcnt, lba, lba_count, io_flags, &spdk_nvme_ns_cmd_readv);
206+
}
207+
208+
} // namespace spdk
209+
} // namespace photon

0 commit comments

Comments
 (0)