Skip to content

Commit 573257d

Browse files
author
Alexander Indenbaum
committed
bdev/rbd: round-robin reactor selection for SpdkContextWQ
Discover reactor threads at module init and assign the next reactor to each new RBD image so SpdkContextWQ is balanced across cores. Signed-off-by: Alexander Indenbaum <aindenba@redhat.com>
1 parent a492b4b commit 573257d

File tree

3 files changed

+88
-47
lines changed

3 files changed

+88
-47
lines changed

module/bdev/rbd/bdev_rbd.c

Lines changed: 0 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,6 @@
2121
#include "spdk/bdev_module.h"
2222
#include "spdk/log.h"
2323

24-
/* to access reactor framework */
25-
#include "spdk_internal/event.h"
26-
#include "spdk_internal/thread.h"
27-
2824
SPDK_LOG_REGISTER_COMPONENT(reservation)
2925

3026
static int bdev_rbd_count = 0;
@@ -478,9 +474,6 @@ bdev_rbd_get_pool_ctx(rados_t *cluster_p, const char *name, const char *namespac
478474
return -1;
479475
}
480476

481-
/* Forward declaration for reactor thread selection function */
482-
static struct spdk_thread *bdev_rbd_find_reactor_thread(void);
483-
484477
static void *
485478
bdev_rbd_init_context(void *arg)
486479
{
@@ -761,46 +754,6 @@ static struct spdk_bdev_module rbd_if = {
761754
};
762755
SPDK_BDEV_MODULE_REGISTER(rbd, &rbd_if)
763756

764-
/**
765-
* Find a reactor thread for SpdkContextWQ.
766-
* Iterates through reactors to find a reactor thread (not app_thread).
767-
* Returns NULL if no suitable thread is found.
768-
*/
769-
static struct spdk_thread *
770-
bdev_rbd_find_reactor_thread(void)
771-
{
772-
uint32_t lcore;
773-
774-
SPDK_ENV_FOREACH_CORE(lcore) {
775-
struct spdk_reactor *reactor = spdk_reactor_get(lcore);
776-
if (reactor == NULL || !reactor->flags.is_valid) {
777-
continue;
778-
}
779-
780-
if (reactor->thread_count == 0) {
781-
continue;
782-
}
783-
784-
struct spdk_lw_thread *lw_thread = TAILQ_FIRST(&reactor->threads);
785-
if (lw_thread == NULL) {
786-
continue;
787-
}
788-
789-
struct spdk_thread *thread = spdk_thread_get_from_ctx(lw_thread);
790-
if (thread == NULL || spdk_thread_is_app_thread(thread)) {
791-
continue;
792-
}
793-
794-
uint64_t thread_id = spdk_thread_get_id(thread);
795-
const char *thread_name = spdk_thread_get_name(thread);
796-
SPDK_NOTICELOG("bdev_rbd_find_reactor_thread: reactor thread lcore=%u: thread=%p, id=%lu, name=%s\n",
797-
lcore, thread, thread_id, thread_name ? thread_name : "NULL");
798-
return thread;
799-
}
800-
801-
return NULL;
802-
}
803-
804757
static int bdev_rbd_reset_timer(void *arg);
805758

806759
static void

module/bdev/rbd/bdev_rbd_spdk_context_wq.cpp

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@
77
#include <rbd/librbd.h>
88
#include <rados/librados.hpp>
99
#include <atomic>
10+
#include <mutex>
11+
#include <vector>
1012

1113
#include "bdev_rbd_spdk_context_wq.h"
1214

@@ -15,8 +17,80 @@ extern "C" {
1517
#include "spdk/thread.h"
1618
#include "spdk/log.h"
1719
#include "spdk/env.h"
20+
#include "spdk_internal/event.h"
1821
}
1922

23+
/**
24+
* Encapsulates the set of reactor threads for round-robin assignment.
25+
* init() discovers reactor threads; it runs once lazily on first
26+
* bdev_rbd_find_reactor_thread() (std::call_once).
27+
*/
28+
class ReactorThreadPool {
29+
public:
30+
/** Ensures reactor list is discovered once (lazy init inside bdev_rbd_find_reactor_thread). */
31+
static void ensure_discovered() {
32+
std::call_once(g_discover_once, init);
33+
}
34+
35+
static void init() {
36+
if (!g_reactor_threads.empty()) {
37+
SPDK_ERRLOG("bdev_rbd: reactor thread pool init skipped, list already has %zu thread(s)\n",
38+
g_reactor_threads.size());
39+
return;
40+
}
41+
uint32_t lcore;
42+
SPDK_ENV_FOREACH_CORE(lcore) {
43+
struct spdk_reactor *reactor = spdk_reactor_get(lcore);
44+
if (reactor == NULL || !reactor->flags.is_valid) {
45+
continue;
46+
}
47+
if (reactor->thread_count == 0) {
48+
continue;
49+
}
50+
struct spdk_lw_thread *lw_thread = TAILQ_FIRST(&reactor->threads);
51+
if (lw_thread == NULL) {
52+
continue;
53+
}
54+
struct spdk_thread *thread = spdk_thread_get_from_ctx(lw_thread);
55+
if (thread == NULL || spdk_thread_is_app_thread(thread)) {
56+
continue;
57+
}
58+
const char *name = spdk_thread_get_name(thread);
59+
SPDK_NOTICELOG("bdev_rbd: discovered reactor thread=%p (id=%lu, name=%s, index=%zu)\n",
60+
thread, spdk_thread_get_id(thread), name ? name : "NULL", g_reactor_threads.size());
61+
g_reactor_threads.push_back(thread);
62+
}
63+
64+
if (!g_reactor_threads.empty()) {
65+
SPDK_NOTICELOG("bdev_rbd: reactor thread pool: discovered %zu reactor(s) for round-robin\n",
66+
g_reactor_threads.size());
67+
}
68+
}
69+
70+
static struct spdk_thread *get_next() {
71+
if (g_reactor_threads.empty()) {
72+
SPDK_ERRLOG("bdev_rbd: reactor thread pool is empty, no reactor threads available for SpdkContextWQ\n");
73+
return NULL;
74+
}
75+
size_t n = g_reactor_threads.size();
76+
size_t idx = g_reactor_thread_next.fetch_add(1, std::memory_order_relaxed) % n;
77+
struct spdk_thread *t = g_reactor_threads[idx];
78+
const char *name = spdk_thread_get_name(t);
79+
SPDK_NOTICELOG("bdev_rbd: next reactor thread=%p (id=%lu, name=%s, index=%zu/%zu)\n",
80+
t, spdk_thread_get_id(t), name ? name : "NULL", idx, n);
81+
return t;
82+
}
83+
84+
private:
85+
static std::vector<struct spdk_thread *> g_reactor_threads;
86+
static std::atomic<uint32_t> g_reactor_thread_next;
87+
static std::once_flag g_discover_once;
88+
};
89+
90+
std::vector<struct spdk_thread *> ReactorThreadPool::g_reactor_threads;
91+
std::atomic<uint32_t> ReactorThreadPool::g_reactor_thread_next{0};
92+
std::once_flag ReactorThreadPool::g_discover_once;
93+
2094
namespace librbd {
2195
namespace asio {
2296

@@ -173,4 +247,10 @@ void bdev_rbd_spdk_context_wq_destroy(struct bdev_rbd_spdk_context_wq* context_w
173247
delete wq;
174248
}
175249

250+
struct spdk_thread* bdev_rbd_find_reactor_thread(void)
251+
{
252+
ReactorThreadPool::ensure_discovered();
253+
return ReactorThreadPool::get_next();
254+
}
255+
176256
} // extern "C"

module/bdev/rbd/bdev_rbd_spdk_context_wq.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,14 @@ struct bdev_rbd_spdk_context_wq* bdev_rbd_spdk_context_wq_create_from_ioctx(rado
4747
*/
4848
void bdev_rbd_spdk_context_wq_destroy(struct bdev_rbd_spdk_context_wq* context_wq);
4949

50+
/**
51+
* Return the next reactor thread for SpdkContextWQ (round-robin).
52+
* Lazy-initializes the reactor list on first call (std::call_once).
53+
* Each call returns a different reactor so RBD images are balanced across reactors.
54+
* Returns NULL if no reactor threads were discovered (error is logged).
55+
*/
56+
struct spdk_thread* bdev_rbd_find_reactor_thread(void);
57+
5058
#ifdef __cplusplus
5159
}
5260

0 commit comments

Comments
 (0)