[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <20251024084259.2359693-4-amarkuze@redhat.com>
Date: Fri, 24 Oct 2025 08:42:57 +0000
From: Alex Markuze <amarkuze@...hat.com>
To: ceph-devel@...r.kernel.org,
linux-kernel@...r.kernel.org,
linux-mm@...ck.org
Cc: Liam.Howlett@...cle.com,
amarkuze@...hat.com,
akpm@...ux-foundation.org,
bsegall@...gle.com,
david@...hat.com,
dietmar.eggemann@....com,
idryomov@...il.com,
mingo@...hat.com,
juri.lelli@...hat.com,
kees@...nel.org,
lorenzo.stoakes@...cle.com,
mgorman@...e.de,
mhocko@...e.com,
rppt@...nel.org,
peterz@...radead.org,
rostedt@...dmis.org,
surenb@...gle.com,
vschneid@...hat.com,
vincent.guittot@...aro.org,
vbabka@...e.cz,
xiubli@...hat.com,
Slava.Dubeyko@....com
Subject: [RFC PATCH 3/5] ceph: Add BLOG scaffolding
Introduce the Ceph BLOG integration layer that enables the filesystem to
register binary loggers and route log events through module-specific TLS
contexts. This commit establishes the infrastructure for high-performance
binary logging in the Ceph client without modifying existing logging calls.
**Key Components:**
**ceph_blog.h - BLOG-aware macros and interfaces:**
- bout/boutc: Drop-in replacements for dout/doutc with identical semantics
- CEPH_BLOG_LOG_CTX/CEPH_BLOG_LOG: Low-level logging primitives
- Automatic fallback to traditional dout when CONFIG_BLOG is disabled
- Type-safe parameter serialization using BLOG framework
**blog_client.c - Module registration and context management:**
- ceph_blog_init(): Register Ceph-specific BLOG logger on module load
- ceph_blog_exit(): Cleanup and unregister logger on module unload
- ceph_get_tls_ctx(): Acquire per-task BLOG context for current thread
- Client-ID to logger mapping for multi-client scenarios
- Exported symbols for context acquisition from Ceph code paths
**Makefile integration:**
- Conditionally build blog_client.o when CONFIG_CEPH_FS && CONFIG_BLOG
- Clean integration with existing Ceph build system
**Design rationale:**
The bout/boutc macros maintain the same call-site syntax as dout/doutc,
allowing gradual migration and easy A/B testing. Log levels and subsystem
filtering remain unchanged. The TLS context is acquired lazily on first use
per task and recycled automatically on task exit via the BLOG lifecycle
hooks wired in earlier commits.
No existing Ceph logging is modified in this commit. The infrastructure is
in place but inactive until logging call sites are converted to bout/boutc
in subsequent commits.
Signed-off-by: Alex Markuze <amarkuze@...hat.com>
---
fs/ceph/Makefile | 2 +
fs/ceph/blog_client.c | 244 +++++++++++++++++++++++++++++++++
include/linux/ceph/ceph_blog.h | 124 +++++++++++++++++
3 files changed, 370 insertions(+)
create mode 100644 fs/ceph/blog_client.c
create mode 100644 include/linux/ceph/ceph_blog.h
diff --git a/fs/ceph/Makefile b/fs/ceph/Makefile
index 1f77ca04c426..ccb542870ab3 100644
--- a/fs/ceph/Makefile
+++ b/fs/ceph/Makefile
@@ -10,6 +10,8 @@ ceph-y := super.o inode.o dir.o file.o locks.o addr.o ioctl.o \
mds_client.o mdsmap.o strings.o ceph_frag.o \
debugfs.o util.o metric.o
+ceph-$(CONFIG_BLOG) += blog_client.o blog_debugfs.o
+
ceph-$(CONFIG_CEPH_FSCACHE) += cache.o
ceph-$(CONFIG_CEPH_FS_POSIX_ACL) += acl.o
ceph-$(CONFIG_FS_ENCRYPTION) += crypto.o
diff --git a/fs/ceph/blog_client.c b/fs/ceph/blog_client.c
new file mode 100644
index 000000000000..2d9c7e37f918
--- /dev/null
+++ b/fs/ceph/blog_client.c
@@ -0,0 +1,244 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Ceph client ID management for BLOG integration
+ *
+ * Maintains mapping between Ceph's fsid/global_id and BLOG client IDs
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/ceph/ceph_debug.h>
+#include <linux/ceph/libceph.h>
+#include <linux/ceph/ceph_blog.h>
+#include <linux/blog/blog.h>
+
+/* Ceph's BLOG module context */
+struct blog_module_context *ceph_blog_ctx;
+EXPORT_SYMBOL(ceph_blog_ctx);
+
+/* Ceph's logger - direct access to the logger from module context */
+struct blog_logger *ceph_logger;
+EXPORT_SYMBOL(ceph_logger);
+
+/* Global client mapping state */
+static struct {
+ struct ceph_blog_client_info client_map[CEPH_BLOG_MAX_CLIENTS];
+ u32 next_client_id;
+ spinlock_t lock; /* protects client_map */
+ bool initialized;
+} ceph_blog_state = {
+ .next_client_id = 1, /* Start from 1, 0 is reserved */
+ .lock = __SPIN_LOCK_UNLOCKED(ceph_blog_state.lock),
+ .initialized = false,
+};
+
+static bool ceph_blog_ids_match(const struct ceph_blog_client_info *entry,
+ const char *fsid, u64 global_id)
+{
+ if (!entry)
+ return false;
+ if (entry->global_id != global_id)
+ return false;
+ return !memcmp(entry->fsid, fsid, sizeof(entry->fsid));
+}
+
+/**
+ * ceph_blog_init - Initialize Ceph BLOG integration
+ *
+ * Creates a module-specific BLOG context for Ceph and initializes
+ * the client ID mapping state.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int ceph_blog_init(void)
+{
+ if (ceph_blog_state.initialized)
+ return 0;
+
+ /* Create Ceph's module-specific BLOG context */
+ ceph_blog_ctx = blog_module_init("ceph");
+ if (!ceph_blog_ctx) {
+ pr_err("ceph: Failed to initialize BLOG module context\n");
+ return -ENOMEM;
+ }
+
+ /* Set ceph_logger for direct access to the logger */
+ ceph_logger = ceph_blog_ctx->logger;
+
+ /* Initialize client mapping state */
+ memset(ceph_blog_state.client_map, 0, sizeof(ceph_blog_state.client_map));
+ ceph_blog_state.next_client_id = 1;
+ ceph_blog_state.initialized = true;
+
+ pr_info("ceph: BLOG module context and client mapping initialized\n");
+ return 0;
+}
+EXPORT_SYMBOL(ceph_blog_init);
+
+/**
+ * ceph_blog_cleanup - Clean up Ceph BLOG integration
+ *
+ * Cleans up Ceph's module-specific BLOG context and client mappings.
+ */
+void ceph_blog_cleanup(void)
+{
+ if (!ceph_blog_state.initialized)
+ return;
+
+ /* Clean up client mapping state */
+ spin_lock(&ceph_blog_state.lock);
+ memset(ceph_blog_state.client_map, 0, sizeof(ceph_blog_state.client_map));
+ ceph_blog_state.next_client_id = 1;
+ ceph_blog_state.initialized = false;
+ spin_unlock(&ceph_blog_state.lock);
+
+ /* Clean up module-specific BLOG context */
+ if (ceph_blog_ctx) {
+ blog_module_cleanup(ceph_blog_ctx);
+ ceph_blog_ctx = NULL;
+ ceph_logger = NULL;
+ }
+
+ pr_info("ceph: BLOG module context and client mapping cleaned up\n");
+}
+EXPORT_SYMBOL(ceph_blog_cleanup);
+
+/**
+ * ceph_blog_check_client_id - Check if a client ID matches the given fsid:global_id pair
+ * @id: Client ID to check
+ * @fsid: Client FSID to compare
+ * @global_id: Client global ID to compare
+ *
+ * This preserves the exact functionality of ceph_san_check_client_id.
+ * Returns the actual ID of the pair. If the given ID doesn't match, scans for
+ * existing matches or allocates a new ID if no match is found.
+ *
+ * Return: Client ID for this fsid/global_id pair
+ */
+u32 ceph_blog_check_client_id(u32 id, const char *fsid, u64 global_id)
+{
+ u32 found_id = 0;
+ struct ceph_blog_client_info *entry;
+
+ if (unlikely(!ceph_blog_state.initialized)) {
+ WARN_ON_ONCE(1); /* Should never happen - init_ceph() initializes BLOG */
+ return 0; /* Drop the log entry */
+ }
+
+ spin_lock(&ceph_blog_state.lock);
+
+ /* Reuse caller-provided hint when it still matches */
+ if (id != 0 && id < CEPH_BLOG_MAX_CLIENTS) {
+ entry = &ceph_blog_state.client_map[id];
+ if (ceph_blog_ids_match(entry, fsid, global_id)) {
+ found_id = id;
+ goto out;
+ }
+ }
+
+ /* Search for an existing entry with matching identity */
+ for (id = 1; id < CEPH_BLOG_MAX_CLIENTS; id++) {
+ entry = &ceph_blog_state.client_map[id];
+ if (ceph_blog_ids_match(entry, fsid, global_id)) {
+ found_id = id;
+ goto out;
+ }
+ }
+
+ /* Assign new identifier, falling back to overflow slot if exhausted */
+ if (ceph_blog_state.next_client_id >= CEPH_BLOG_MAX_CLIENTS - 1) {
+ found_id = CEPH_BLOG_MAX_CLIENTS - 1;
+ entry = &ceph_blog_state.client_map[found_id];
+ } else {
+ found_id = ceph_blog_state.next_client_id++;
+ entry = &ceph_blog_state.client_map[found_id];
+ }
+
+ memset(entry, 0, sizeof(*entry));
+ memcpy(entry->fsid, fsid, sizeof(entry->fsid));
+ entry->global_id = global_id;
+
+ pr_info("ceph: allocating new BLOG client ID %u for fsid=%pU global_id=%llu\n",
+ found_id, fsid, global_id);
+
+out:
+ spin_unlock(&ceph_blog_state.lock);
+ return found_id;
+}
+EXPORT_SYMBOL(ceph_blog_check_client_id);
+
+/**
+ * ceph_blog_get_client_info - Get client info for a given ID
+ * @id: Client ID
+ *
+ * Return: Client information for this ID, or NULL if invalid
+ */
+const struct ceph_blog_client_info *ceph_blog_get_client_info(u32 id)
+{
+ if (!ceph_blog_state.initialized || id == 0 || id >= CEPH_BLOG_MAX_CLIENTS)
+ return NULL;
+ return &ceph_blog_state.client_map[id];
+}
+EXPORT_SYMBOL(ceph_blog_get_client_info);
+
+/**
+ * ceph_blog_client_des_callback - Deserialization callback for Ceph client info
+ * @buf: Output buffer
+ * @size: Buffer size
+ * @client_id: Client ID to deserialize
+ *
+ * This is the callback that BLOG will use to deserialize client information.
+ *
+ * Return: Number of bytes written to buffer
+ */
+int ceph_blog_client_des_callback(char *buf, size_t size, u8 client_id)
+{
+ const struct ceph_blog_client_info *info;
+
+ if (!buf || !size)
+ return -EINVAL;
+
+ info = ceph_blog_get_client_info(client_id);
+ if (!info)
+ return snprintf(buf, size, "[unknown_client_%u]", client_id);
+
+ /* Use %pU to format fsid, matching boutc and other Ceph client logging */
+ return snprintf(buf, size, "[%pU %llu] ",
+ info->fsid, info->global_id);
+}
+EXPORT_SYMBOL(ceph_blog_client_des_callback);
+
+/**
+ * ceph_blog_get_client_id - Get or allocate client ID for a Ceph client
+ * @client: Ceph client structure
+ *
+ * Return: Client ID for this client
+ */
+u32 ceph_blog_get_client_id(struct ceph_client *client)
+{
+ u32 cached = 0;
+ u32 id;
+
+ if (!client)
+ return 0;
+
+#ifdef CONFIG_BLOG
+ cached = READ_ONCE(client->blog_client_id);
+#endif
+
+ id = ceph_blog_check_client_id(cached,
+ client->fsid.fsid,
+ client->monc.auth->global_id);
+ if (!id)
+ return 0;
+
+#ifdef CONFIG_BLOG
+ if (READ_ONCE(client->blog_client_id) != id)
+ WRITE_ONCE(client->blog_client_id, id);
+#endif
+
+ return id;
+}
+EXPORT_SYMBOL(ceph_blog_get_client_id);
diff --git a/include/linux/ceph/ceph_blog.h b/include/linux/ceph/ceph_blog.h
new file mode 100644
index 000000000000..0591e3f29703
--- /dev/null
+++ b/include/linux/ceph/ceph_blog.h
@@ -0,0 +1,124 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Ceph integration with BLOG (Binary LOGging)
+ *
+ * Provides compatibility layer and Ceph-specific extensions
+ */
+#ifndef CEPH_BLOG_H
+#define CEPH_BLOG_H
+
+#include <linux/blog/blog.h>
+#include <linux/blog/blog_module.h>
+#include <linux/ceph/libceph.h>
+
+/* Client ID mapping structure - preserves ceph_san_client_id fields */
+struct ceph_blog_client_info {
+ char fsid[16]; /* Client FSID */
+ u64 global_id; /* Client global ID */
+};
+
+/* Constants */
+#define CEPH_BLOG_MAX_CLIENTS 256
+
+/* Ceph's BLOG module context */
+extern struct blog_module_context *ceph_blog_ctx;
+
+/* Ceph's logger - direct access to the logger for macros */
+extern struct blog_logger *ceph_logger;
+
+/* Forward declaration for ceph_client */
+struct ceph_client;
+
+/* Compatibility macros for easy migration from ceph_san to BLOG */
+#if IS_ENABLED(CONFIG_BLOG)
+
+/* Ceph BLOG client management functions */
+int ceph_blog_init(void);
+void ceph_blog_cleanup(void);
+u32 ceph_blog_check_client_id(u32 id, const char *fsid, u64 global_id);
+u32 ceph_blog_get_client_id(struct ceph_client *client);
+const struct ceph_blog_client_info *ceph_blog_get_client_info(u32 id);
+int ceph_blog_client_des_callback(char *buf, size_t size, u8 client_id);
+
+/*
+ * All ceph_san compatibility removed - use only BLOG with per-module contexts
+ * CEPH_SAN has been replaced entirely by BLOG per-module logging
+ */
+
+/*
+ * Ceph-specific logging macros - use core BLOG functions with ceph_logger
+ * Note: Only client-aware macros (doutc, boutc) store client_id,
+ * regular macros (dout, bout) do not include client information
+ */
+#define CEPH_BLOG_LOG(fmt, ...) \
+ do { \
+ static u32 __source_id; \
+ static size_t __size; \
+ void *___buffer = NULL; \
+ if (unlikely(!ceph_logger)) \
+ break; \
+ if (unlikely(__source_id == 0)) { \
+ __source_id = blog_get_source_id(ceph_logger, \
+ kbasename(__FILE__), __func__, __LINE__, fmt); \
+ __size = blog_cnt(__VA_ARGS__); \
+ } \
+ ___buffer = blog_log(ceph_logger, __source_id, 0, __size); \
+ if (likely(___buffer)) { \
+ void *___tmp = ___buffer; \
+ if (__size > 0) \
+ blog_ser(___buffer, ##__VA_ARGS__); \
+ blog_log_commit(ceph_logger, ___buffer - ___tmp); \
+ } \
+ } while (0)
+
+#define CEPH_BLOG_LOG_CLIENT(client, fmt, ...) \
+ do { \
+ static u32 __source_id; \
+ static size_t __size; \
+ void *___buffer = NULL; \
+ u32 __client_id; \
+ if (unlikely(!ceph_logger)) \
+ break; \
+ if (unlikely(__source_id == 0)) { \
+ __source_id = blog_get_source_id(ceph_logger, \
+ kbasename(__FILE__), __func__, __LINE__, fmt); \
+ __size = blog_cnt(__VA_ARGS__); \
+ } \
+ __client_id = ceph_blog_get_client_id(client); \
+ ___buffer = blog_log(ceph_logger, __source_id, __client_id, __size); \
+ if (likely(___buffer)) { \
+ void *___tmp = ___buffer; \
+ if (__size > 0) \
+ blog_ser(___buffer, ##__VA_ARGS__); \
+ blog_log_commit(ceph_logger, ___buffer - ___tmp); \
+ } \
+ } while (0)
+
+/* No legacy ceph_san compatibility - use CEPH_BLOG_LOG* macros only */
+
+#else /* !CONFIG_BLOG */
+
+/* Stub macros when BLOG is not enabled */
+#define CEPH_BLOG_LOG(fmt, ...) do {} while (0)
+#define CEPH_BLOG_LOG_CLIENT(client, fmt, ...) do {} while (0)
+
+/* Stub functions should be static inline, not macros */
+static inline int ceph_blog_init(void) { return 0; }
+static inline void ceph_blog_cleanup(void) { }
+static inline u32 ceph_blog_get_client_id(struct ceph_client *client) { return 0; }
+static inline u32 ceph_blog_check_client_id(u32 id, const char *fsid, u64 global_id) { return 0; }
+static inline const struct ceph_blog_client_info *ceph_blog_get_client_info(u32 id) { return NULL; }
+static inline int ceph_blog_client_des_callback(char *buf, size_t size, u8 client_id) { return 0; }
+
+#endif /* CONFIG_BLOG */
+
+/* Debugfs support */
+#ifdef CONFIG_DEBUG_FS
+int ceph_blog_debugfs_init(struct dentry *parent);
+void ceph_blog_debugfs_cleanup(void);
+#else
+static inline int ceph_blog_debugfs_init(struct dentry *parent) { return 0; }
+static inline void ceph_blog_debugfs_cleanup(void) {}
+#endif
+
+#endif /* CEPH_BLOG_H */
--
2.34.1
Powered by blists - more mailing lists