[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <b6d9c32721a5b836a84945232d98aab1d15e3638.camel@ibm.com>
Date: Mon, 3 Nov 2025 22:37:15 +0000
From: Viacheslav Dubeyko <Slava.Dubeyko@....com>
To: "linux-mm@...ck.org" <linux-mm@...ck.org>,
Alex Markuze
<amarkuze@...hat.com>,
"ceph-devel@...r.kernel.org"
<ceph-devel@...r.kernel.org>,
"linux-kernel@...r.kernel.org"
<linux-kernel@...r.kernel.org>
CC: "dietmar.eggemann@....com" <dietmar.eggemann@....com>,
"rppt@...nel.org"
<rppt@...nel.org>,
"lorenzo.stoakes@...cle.com" <lorenzo.stoakes@...cle.com>,
Xiubo Li <xiubli@...hat.com>,
"idryomov@...il.com" <idryomov@...il.com>,
"david@...hat.com" <david@...hat.com>,
"mgorman@...e.de" <mgorman@...e.de>, "vbabka@...e.cz" <vbabka@...e.cz>,
"vincent.guittot@...aro.org"
<vincent.guittot@...aro.org>,
"akpm@...ux-foundation.org"
<akpm@...ux-foundation.org>,
"Liam.Howlett@...cle.com"
<Liam.Howlett@...cle.com>,
Ingo Molnar <mingo@...hat.com>,
"rostedt@...dmis.org" <rostedt@...dmis.org>,
"surenb@...gle.com"
<surenb@...gle.com>,
Valentin Schneider <vschneid@...hat.com>,
"kees@...nel.org" <kees@...nel.org>,
"peterz@...radead.org"
<peterz@...radead.org>,
"mhocko@...e.com" <mhocko@...e.com>,
"bsegall@...gle.com" <bsegall@...gle.com>,
"juri.lelli@...hat.com"
<juri.lelli@...hat.com>
Subject: Re: [RFC PATCH 3/5] ceph: Add BLOG scaffolding
On Fri, 2025-10-24 at 08:42 +0000, Alex Markuze wrote:
> 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.
>
Frankly speaking, the kernel subsystems' integration logic requires a lot of
code. I expected that it will be much easier to integrate the BLOG
infrastructure into a particular kernel subsystem. Is it possible to move
majority of the functionality into the generalized BLOG infrastructure? I
believe that everyone will love really simple integration logic.
Thanks,
Slava.
> **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 */
Powered by blists - more mailing lists