Although we've recently added nbdkit_next_context_open to make it
easier to access a plugin from a filter, to date we have still been
limited to opening a context while associated with a client
connection, because backend_open uses GET_CONN which asserts there is
an active connection. In order to lift that restriction, we will need
to be able to allow a context that has no associated connection, and
move the data that feeds nbdkit_export_name() and friends out of the
connection and into the context. This patch starts the process, by
updating the thread local storage to track the current context, as
well as all calls into backend.c to keep this information up-to-date.
As this is internal to the server, I did not think it proper to add
this into common/utils/cleanup.h, but rather just placed the new
macros in internal.h.
---
server/internal.h | 7 +++++++
server/backend.c | 23 +++++++++++++++++++++++
server/threadlocal.c | 27 ++++++++++++++++++++++++---
3 files changed, 54 insertions(+), 3 deletions(-)
diff --git a/server/internal.h b/server/internal.h
index 849edbf8..a8283195 100644
--- a/server/internal.h
+++ b/server/internal.h
@@ -527,6 +527,13 @@ extern void *threadlocal_buffer (size_t size);
extern void threadlocal_set_conn (struct connection *conn);
extern struct connection *threadlocal_get_conn (void);
+extern struct context *threadlocal_push_context (struct context *ctx);
+extern void threadlocal_pop_context (struct context **ctx);
+#define CLEANUP_CONTEXT_POP __attribute__((cleanup (threadlocal_pop_context)))
+#define PUSH_CONTEXT_FOR_SCOPE(ctx) \
+ CLEANUP_CONTEXT_POP struct context *UNIQUE_VAR(_ctx) = \
+ threadlocal_push_context (ctx)
+
/* Macro which sets local variable struct connection *conn from
* thread-local storage, asserting that it is non-NULL. If you want
* to check if conn could be NULL (eg. outside a connection context)
diff --git a/server/backend.c b/server/backend.c
index 69625597..2244df7a 100644
--- a/server/backend.c
+++ b/server/backend.c
@@ -238,6 +238,7 @@ backend_open (struct backend *b, int readonly, const char
*exportname)
{
GET_CONN;
struct context *c = malloc (sizeof *c);
+ PUSH_CONTEXT_FOR_SCOPE (c);
if (c == NULL) {
nbdkit_error ("malloc: %m");
@@ -294,6 +295,7 @@ backend_open (struct backend *b, int readonly, const char
*exportname)
int
backend_prepare (struct context *c)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
assert (c->handle);
@@ -317,6 +319,7 @@ backend_prepare (struct context *c)
int
backend_finalize (struct context *c)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
/* Call these in reverse order to .prepare above, starting from the
@@ -344,6 +347,7 @@ backend_finalize (struct context *c)
void
backend_close (struct context *c)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
struct context *c_next = c->c_next;
@@ -370,6 +374,7 @@ backend_valid_range (struct context *c, uint64_t offset, uint32_t
count)
const char *
backend_export_description (struct context *c)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
const char *s;
@@ -391,6 +396,7 @@ backend_export_description (struct context *c)
int64_t
backend_get_size (struct context *c)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
assert (c->handle && (c->state & HANDLE_CONNECTED));
@@ -404,6 +410,7 @@ backend_get_size (struct context *c)
int
backend_can_write (struct context *c)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
assert (c->handle && (c->state & HANDLE_CONNECTED));
@@ -417,6 +424,7 @@ backend_can_write (struct context *c)
int
backend_can_flush (struct context *c)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
assert (c->handle && (c->state & HANDLE_CONNECTED));
@@ -430,6 +438,7 @@ backend_can_flush (struct context *c)
int
backend_is_rotational (struct context *c)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
assert (c->handle && (c->state & HANDLE_CONNECTED));
@@ -443,6 +452,7 @@ backend_is_rotational (struct context *c)
int
backend_can_trim (struct context *c)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
int r;
@@ -462,6 +472,7 @@ backend_can_trim (struct context *c)
int
backend_can_zero (struct context *c)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
int r;
@@ -481,6 +492,7 @@ backend_can_zero (struct context *c)
int
backend_can_fast_zero (struct context *c)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
int r;
@@ -500,6 +512,7 @@ backend_can_fast_zero (struct context *c)
int
backend_can_extents (struct context *c)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
assert (c->handle && (c->state & HANDLE_CONNECTED));
@@ -513,6 +526,7 @@ backend_can_extents (struct context *c)
int
backend_can_fua (struct context *c)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
int r;
@@ -532,6 +546,7 @@ backend_can_fua (struct context *c)
int
backend_can_multi_conn (struct context *c)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
assert (c->handle && (c->state & HANDLE_CONNECTED));
@@ -545,6 +560,7 @@ backend_can_multi_conn (struct context *c)
int
backend_can_cache (struct context *c)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
assert (c->handle && (c->state & HANDLE_CONNECTED));
@@ -560,6 +576,7 @@ backend_pread (struct context *c,
void *buf, uint32_t count, uint64_t offset,
uint32_t flags, int *err)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
int r;
@@ -580,6 +597,7 @@ backend_pwrite (struct context *c,
const void *buf, uint32_t count, uint64_t offset,
uint32_t flags, int *err)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
bool fua = !!(flags & NBDKIT_FLAG_FUA);
int r;
@@ -603,6 +621,7 @@ int
backend_flush (struct context *c,
uint32_t flags, int *err)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
int r;
@@ -622,6 +641,7 @@ backend_trim (struct context *c,
uint32_t count, uint64_t offset, uint32_t flags,
int *err)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
bool fua = !!(flags & NBDKIT_FLAG_FUA);
int r;
@@ -647,6 +667,7 @@ backend_zero (struct context *c,
uint32_t count, uint64_t offset, uint32_t flags,
int *err)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
bool fua = !!(flags & NBDKIT_FLAG_FUA);
bool fast = !!(flags & NBDKIT_FLAG_FAST_ZERO);
@@ -681,6 +702,7 @@ backend_extents (struct context *c,
uint32_t count, uint64_t offset, uint32_t flags,
struct nbdkit_extents *extents, int *err)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
int r;
@@ -711,6 +733,7 @@ backend_cache (struct context *c,
uint32_t count, uint64_t offset,
uint32_t flags, int *err)
{
+ PUSH_CONTEXT_FOR_SCOPE (c);
struct backend *b = c->b;
int r;
diff --git a/server/threadlocal.c b/server/threadlocal.c
index 90230028..644239b7 100644
--- a/server/threadlocal.c
+++ b/server/threadlocal.c
@@ -1,5 +1,5 @@
/* nbdkit
- * Copyright (C) 2013-2020 Red Hat Inc.
+ * Copyright (C) 2013-2021 Red Hat Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
@@ -56,9 +56,10 @@ struct threadlocal {
char *name; /* Can be NULL. */
size_t instance_num; /* Can be 0. */
int err;
- void *buffer;
+ void *buffer; /* Can be NULL. */
size_t buffer_size;
- struct connection *conn;
+ struct connection *conn; /* Can be NULL. */
+ struct context *ctx; /* Can be NULL. */
};
static pthread_key_t threadlocal_key;
@@ -227,3 +228,23 @@ threadlocal_get_conn (void)
return threadlocal ? threadlocal->conn : NULL;
}
+
+/* Set (or clear) the context using the current thread */
+struct context *
+threadlocal_push_context (struct context *ctx)
+{
+ struct threadlocal *threadlocal = pthread_getspecific (threadlocal_key);
+ struct context *ret = NULL;
+
+ if (threadlocal) {
+ ret = threadlocal->ctx;
+ threadlocal->ctx = ctx;
+ }
+ return ret;
+}
+
+void
+threadlocal_pop_context (struct context **ctx)
+{
+ threadlocal_push_context (*ctx);
+}
--
2.31.1