linux-mm.kvack.org archive mirror
 help / color / mirror / Atom feed
From: Alex Markuze <amarkuze@redhat.com>
To: ceph-devel@vger.kernel.org, linux-kernel@vger.kernel.org,
	linux-mm@kvack.org
Cc: Liam.Howlett@oracle.com, amarkuze@redhat.com,
	akpm@linux-foundation.org, bsegall@google.com, david@redhat.com,
	dietmar.eggemann@arm.com, idryomov@gmail.com, mingo@redhat.com,
	juri.lelli@redhat.com, kees@kernel.org,
	lorenzo.stoakes@oracle.com, mgorman@suse.de, mhocko@suse.com,
	rppt@kernel.org, peterz@infradead.org, rostedt@goodmis.org,
	surenb@google.com, vschneid@redhat.com,
	vincent.guittot@linaro.org, vbabka@suse.cz, xiubli@redhat.com,
	Slava.Dubeyko@ibm.com
Subject: [RFC PATCH 3/5] ceph: Add BLOG scaffolding
Date: Fri, 24 Oct 2025 08:42:57 +0000	[thread overview]
Message-ID: <20251024084259.2359693-4-amarkuze@redhat.com> (raw)
In-Reply-To: <20251024084259.2359693-1-amarkuze@redhat.com>

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@redhat.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



  parent reply	other threads:[~2025-10-24  8:43 UTC|newest]

Thread overview: 19+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2025-10-24  8:42 [RFC PATCH 0/5] BLOG: per-task logging contexts with Ceph consumer Alex Markuze
2025-10-24  8:42 ` [RFC PATCH 1/5] sched, fork: Wire BLOG contexts into task lifecycle Alex Markuze
2025-10-24 17:44   ` Steven Rostedt
2025-10-29 18:57   ` Viacheslav Dubeyko
2025-10-24  8:42 ` [RFC PATCH 2/5] lib: Introduce BLOG (Binary LOGging) subsystem Alex Markuze
2025-10-30 18:47   ` Viacheslav Dubeyko
2025-10-24  8:42 ` Alex Markuze [this message]
2025-11-03 22:37   ` [RFC PATCH 3/5] ceph: Add BLOG scaffolding Viacheslav Dubeyko
2025-10-24  8:42 ` [RFC PATCH 4/5] ceph: Add BLOG debugfs support Alex Markuze
2025-11-03 21:07   ` Viacheslav Dubeyko
2025-10-24  8:42 ` [RFC PATCH 5/5] ceph: Activate BLOG logging Alex Markuze
2025-11-03 21:00   ` Viacheslav Dubeyko
2025-10-24 15:32 ` [RFC PATCH 0/5] BLOG: per-task logging contexts with Ceph consumer David Hildenbrand
2025-10-24 17:53 ` Steven Rostedt
2025-10-25 10:50   ` Alex Markuze
2025-10-25 14:59     ` Steven Rostedt
2025-10-25 17:54       ` Alex Markuze
2025-10-27 14:54         ` Steven Rostedt
2025-10-28 17:07 ` Viacheslav Dubeyko

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20251024084259.2359693-4-amarkuze@redhat.com \
    --to=amarkuze@redhat.com \
    --cc=Liam.Howlett@oracle.com \
    --cc=Slava.Dubeyko@ibm.com \
    --cc=akpm@linux-foundation.org \
    --cc=bsegall@google.com \
    --cc=ceph-devel@vger.kernel.org \
    --cc=david@redhat.com \
    --cc=dietmar.eggemann@arm.com \
    --cc=idryomov@gmail.com \
    --cc=juri.lelli@redhat.com \
    --cc=kees@kernel.org \
    --cc=linux-kernel@vger.kernel.org \
    --cc=linux-mm@kvack.org \
    --cc=lorenzo.stoakes@oracle.com \
    --cc=mgorman@suse.de \
    --cc=mhocko@suse.com \
    --cc=mingo@redhat.com \
    --cc=peterz@infradead.org \
    --cc=rostedt@goodmis.org \
    --cc=rppt@kernel.org \
    --cc=surenb@google.com \
    --cc=vbabka@suse.cz \
    --cc=vincent.guittot@linaro.org \
    --cc=vschneid@redhat.com \
    --cc=xiubli@redhat.com \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox