[PATCH v2 01/15] ceph: add BLOG public headers

Alex Markuze <[email protected]> Mon, 6 Jul 2026 14:38:29 +0000
Newsgroups org.kernel.vger.ceph-devel
Message-ID <[email protected]>
Add the public header files for the binary logging (BLOG) subsystem:

  blog.h          - core types: blog_logger, blog_log_entry,
                    blog_tls_ctx, blog_source_info, blog_log_iter,
                    blog_source_id_cache; core API prototypes
  blog_ser.h      - type-safe serialization macros (blog_ser,
                    blog_cnt) with compile-time type dispatch
  blog_des.h      - deserialization prototypes
  blog_batch.h    - per-CPU magazine batch allocator types
  blog_pagefrag.h - page-fragment allocator types
  blog_module.h   - per-module context types (blog_task_entry,
                    blog_module_context) and the __BLOG_LOG_CTX
                    macro that drives the per-callsite fast path

All headers live under include/linux/ceph/ since BLOG is consumed
exclusively by the CephFS client.

Signed-off-by: Alex Markuze <[email protected]>
---
 include/linux/ceph/blog.h          | 196 +++++++++++++++++++++
 include/linux/ceph/blog_batch.h    |  44 +++++
 include/linux/ceph/blog_des.h      |  16 ++
 include/linux/ceph/blog_module.h   |  91 ++++++++++
 include/linux/ceph/blog_pagefrag.h |  32 ++++
 include/linux/ceph/blog_ser.h      | 270 +++++++++++++++++++++++++++++
 6 files changed, 649 insertions(+)
 create mode 100644 include/linux/ceph/blog.h
 create mode 100644 include/linux/ceph/blog_batch.h
 create mode 100644 include/linux/ceph/blog_des.h
 create mode 100644 include/linux/ceph/blog_module.h
 create mode 100644 include/linux/ceph/blog_pagefrag.h
 create mode 100644 include/linux/ceph/blog_ser.h

diff --git a/include/linux/ceph/blog.h b/include/linux/ceph/blog.h
new file mode 100644
index 000000000000..edc0e1aa05bf
--- /dev/null
+++ b/include/linux/ceph/blog.h
@@ -0,0 +1,196 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Binary Logging Infrastructure (BLOG)
+ *
+ * Generic binary logging infrastructure for kernel subsystems.
+ * Modules maintain their own client mappings and debugfs interfaces.
+ */
+#ifndef _LINUX_CEPH_BLOG_H
+#define _LINUX_CEPH_BLOG_H
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/rhashtable-types.h>
+#include <linux/ceph/blog_batch.h>
+#include <linux/ceph/blog_pagefrag.h>
+#include <linux/ceph/blog_ser.h>
+#include <linux/ceph/blog_des.h>
+
+struct blog_module_context;
+
+#ifdef CONFIG_BLOG_DEBUG
+#define BLOG_DEBUG_POISON 1
+#else
+#define BLOG_DEBUG_POISON 0
+#endif
+
+#ifdef CONFIG_BLOG_TRACK_USAGE
+#define BLOG_TRACK_USAGE 1
+#else
+#define BLOG_TRACK_USAGE 0
+#endif
+
+#if BLOG_DEBUG_POISON
+#define BLOG_LOG_ENTRY_POISON 0xD1E7C0DE
+#define BLOG_CTX_POISON 0xCAFEBABE
+#endif
+
+#define BLOG_MAX_PAYLOAD 255
+#ifdef CONFIG_BLOG_MAX_SOURCES
+#define BLOG_MAX_SOURCE_IDS CONFIG_BLOG_MAX_SOURCES
+#else
+#define BLOG_MAX_SOURCE_IDS 4096
+#endif
+#ifdef CONFIG_BLOG_MAX_CLIENTS
+#define BLOG_MAX_CLIENT_IDS CONFIG_BLOG_MAX_CLIENTS
+#else
+#define BLOG_MAX_CLIENT_IDS 256
+#endif
+
+struct blog_source_info {
+	const char *file;
+	const char *func;
+	unsigned int line;
+	const char *fmt;
+	int warn_count;
+#if BLOG_TRACK_USAGE
+	atomic_t napi_usage;
+	atomic_t task_usage;
+	atomic_t napi_bytes;
+	atomic_t task_bytes;
+#endif
+};
+
+struct blog_log_entry {
+#if BLOG_DEBUG_POISON
+	u64 debug_poison;
+#endif
+	u32 ts_delta;
+	u16 source_id;
+	u8 len;
+	u8 client_id;
+	u8 flags;
+	char buffer[];
+};
+
+#define BLOG_CTX_NEEDS_RESET	0
+
+struct blog_tls_ctx {
+	struct list_head list;
+	void (*release)(void *data);
+	atomic_t refcount;
+	struct task_struct *task;
+	pid_t pid;
+	char comm[TASK_COMM_LEN];
+	u64 id;
+#if BLOG_DEBUG_POISON
+	u64 debug_poison;
+#endif
+	unsigned long base_jiffies;
+	struct blog_logger *logger;
+	int pending_offset;
+	size_t pending_size;
+	unsigned long flags;
+};
+
+struct blog_tls_pagefrag {
+	struct blog_tls_ctx ctx;
+	struct blog_pagefrag pf;
+	unsigned char buf[];
+};
+
+#define BLOG_TLS_PAGEFRAG_ALLOC_SIZE BLOG_PAGEFRAG_SIZE
+#define BLOG_TLS_PAGEFRAG_BUFFER_SIZE \
+	(BLOG_PAGEFRAG_SIZE - sizeof(struct blog_tls_pagefrag))
+
+struct blog_logger {
+	struct list_head contexts;
+	spinlock_t lock;
+	struct rhashtable task_map;
+	struct blog_batch alloc_batch;
+	struct blog_batch log_batch;
+	struct kmem_cache *magazine_cache;
+	struct blog_source_info *source_map;
+	atomic_t next_source_id;
+	spinlock_t source_lock;
+	unsigned long total_contexts_allocated;
+	u64 next_ctx_id;
+	spinlock_t ctx_id_lock;
+	struct blog_tls_ctx *__percpu
+		*napi_ctxs;
+	struct blog_module_context *owner_ctx;
+	u64 generation;
+};
+
+/**
+ * struct blog_source_id_cache - per-callsite source ID fast cache
+ * @logger: logger that owns the cached ID
+ * @generation: logger generation at cache-fill time
+ * @id: cached source ID (0 = not yet cached)
+ */
+struct blog_source_id_cache {
+	struct blog_logger *logger;
+	u64 generation;
+	u32 id;
+};
+
+struct blog_log_iter {
+	struct blog_pagefrag *pf;
+	u64 current_offset;
+	u64 end_offset;
+	u64 prev_offset;
+	u64 steps;
+};
+
+typedef int (*blog_client_des_fn)(char *buf, size_t size, u8 client_id);
+
+u32 blog_get_source_id(struct blog_logger *logger, const char *file,
+		       const char *func, unsigned int line, const char *fmt);
+u32 blog_get_source_id_cached(struct blog_logger *logger,
+			      struct blog_source_id_cache *cache,
+			      const char *file, const char *func,
+			      unsigned int line, const char *fmt);
+struct blog_source_info *blog_get_source_info(struct blog_logger *logger,
+					      u32 id);
+void *blog_log(struct blog_logger *logger, u32 source_id, u8 client_id,
+	       size_t needed_size);
+int blog_log_commit(struct blog_logger *logger, size_t actual_size);
+struct blog_tls_ctx *blog_get_tls_ctx(struct blog_logger *logger);
+struct blog_tls_ctx *blog_get_napi_ctx(struct blog_logger *logger);
+void blog_set_napi_ctx(struct blog_logger *logger, struct blog_tls_ctx *ctx);
+struct blog_tls_ctx *blog_get_ctx(struct blog_logger *logger);
+void blog_log_iter_init(struct blog_log_iter *iter, struct blog_pagefrag *pf,
+			u64 head_snapshot);
+struct blog_log_entry *blog_log_iter_next(struct blog_log_iter *iter);
+int blog_des_entry(struct blog_logger *logger, struct blog_log_entry *entry,
+		   char *output, size_t out_size,
+		   blog_client_des_fn client_cb);
+bool blog_is_valid_kernel_addr(const void *addr);
+
+static inline void blog_logger_print_stats(struct blog_logger *logger)
+{
+	pr_debug(
+		"blog: total_contexts=%lu, alloc_batch={empty=%d, full=%d}, log_batch={empty=%d, full=%d}\n",
+		logger->total_contexts_allocated, logger->alloc_batch.nr_empty,
+		logger->alloc_batch.nr_full, logger->log_batch.nr_empty,
+		logger->log_batch.nr_full);
+}
+
+static inline struct blog_tls_pagefrag *blog_ctx_container(struct blog_tls_ctx *ctx)
+{
+	return container_of(ctx, struct blog_tls_pagefrag, ctx);
+}
+
+static inline void *blog_ctx_buffer(struct blog_tls_ctx *ctx)
+{
+	return blog_ctx_container(ctx)->buf;
+}
+
+static inline struct blog_pagefrag *blog_ctx_pf(struct blog_tls_ctx *ctx)
+{
+	return &blog_ctx_container(ctx)->pf;
+}
+
+#endif /* _LINUX_CEPH_BLOG_H */
diff --git a/include/linux/ceph/blog_batch.h b/include/linux/ceph/blog_batch.h
new file mode 100644
index 000000000000..be61ebd37c17
--- /dev/null
+++ b/include/linux/ceph/blog_batch.h
@@ -0,0 +1,44 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Binary Logging Batch Management
+ */
+#ifndef _LINUX_CEPH_BLOG_BATCH_H
+#define _LINUX_CEPH_BLOG_BATCH_H
+
+#include <linux/types.h>
+#include <linux/percpu.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+
+#define BLOG_MAGAZINE_SIZE 16
+
+struct blog_magazine {
+	struct list_head list;
+	unsigned int count;
+	void *elements[BLOG_MAGAZINE_SIZE];
+};
+
+struct blog_cpu_magazine {
+	struct blog_magazine *mag;
+};
+
+struct blog_batch {
+	struct list_head full_magazines;
+	struct list_head empty_magazines;
+	spinlock_t full_lock;
+	spinlock_t empty_lock;
+	unsigned int nr_full;
+	unsigned int nr_empty;
+	struct blog_cpu_magazine __percpu *cpu_magazines;
+	struct kmem_cache *magazine_cache;
+	bool external_cache;
+	unsigned int retain_limit;
+};
+
+int blog_batch_init(struct blog_batch *batch, struct kmem_cache *mag_cache,
+		    unsigned int nr_prealloc, unsigned int retain_limit);
+void blog_batch_cleanup(struct blog_batch *batch);
+void *blog_batch_get(struct blog_batch *batch);
+void blog_batch_put(struct blog_batch *batch, void *element);
+
+#endif /* _LINUX_CEPH_BLOG_BATCH_H */
diff --git a/include/linux/ceph/blog_des.h b/include/linux/ceph/blog_des.h
new file mode 100644
index 000000000000..a5ce1ed6290a
--- /dev/null
+++ b/include/linux/ceph/blog_des.h
@@ -0,0 +1,16 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Binary Logging Deserialization
+ */
+#ifndef _LINUX_CEPH_BLOG_DES_H
+#define _LINUX_CEPH_BLOG_DES_H
+
+#include <linux/types.h>
+
+struct blog_log_entry;
+struct blog_logger;
+
+int blog_des_reconstruct(const char *fmt, const void *buffer,
+			 size_t size, char *out, size_t out_size);
+
+#endif /* _LINUX_CEPH_BLOG_DES_H */
diff --git a/include/linux/ceph/blog_module.h b/include/linux/ceph/blog_module.h
new file mode 100644
index 000000000000..41651f6bc742
--- /dev/null
+++ b/include/linux/ceph/blog_module.h
@@ -0,0 +1,91 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Binary Logging Infrastructure (BLOG) - Per-Module Support
+ *
+ * Each kernel module can have its own isolated logging context.
+ * Per-task context association uses an rhashtable keyed by task pointer,
+ * with PID validation for stale-entry detection.
+ */
+#ifndef _LINUX_CEPH_BLOG_MODULE_H
+#define _LINUX_CEPH_BLOG_MODULE_H
+
+#include <linux/ceph/blog.h>
+#include <linux/rhashtable.h>
+
+struct blog_task_entry {
+	struct rhash_head	node;
+	struct task_struct	*task;
+	pid_t			pid;
+	char			comm[TASK_COMM_LEN];
+	struct blog_tls_ctx	*ctx;
+	struct rcu_head		rcu;
+};
+
+struct blog_module_context {
+	char name[32];
+	struct blog_logger *logger;
+	void *module_private;
+	struct list_head list;
+	atomic_t refcount;
+	atomic_t allocated_contexts;
+	bool initialized;
+};
+
+struct blog_module_context *blog_module_init(const char *module_name);
+void blog_module_cleanup(struct blog_module_context *ctx);
+void blog_module_get(struct blog_module_context *ctx);
+void blog_module_put(struct blog_module_context *ctx);
+
+struct blog_tls_ctx *blog_get_tls_ctx_ctx(struct blog_module_context *ctx);
+struct blog_tls_ctx *blog_get_napi_ctx_ctx(struct blog_module_context *ctx);
+void blog_set_napi_ctx_ctx(struct blog_module_context *ctx,
+			   struct blog_tls_ctx *tls_ctx);
+struct blog_tls_ctx *blog_get_ctx_ctx(struct blog_module_context *ctx);
+
+u32 blog_get_source_id_ctx(struct blog_module_context *ctx, const char *file,
+			   const char *func, unsigned int line, const char *fmt);
+struct blog_source_info *blog_get_source_info_ctx(struct blog_module_context *ctx,
+						  u32 id);
+
+void *blog_log_with_ctx(struct blog_logger *logger,
+			struct blog_tls_ctx *tls_ctx,
+			u32 source_id, u8 client_id, size_t needed_size);
+int blog_log_commit_with_ctx(struct blog_logger *logger,
+			     struct blog_tls_ctx *tls_ctx,
+			     size_t actual_size);
+
+#define BLOG_LOG_CTX(ctx, fmt, ...) \
+	__BLOG_LOG_CTX(ctx, 0, 0, fmt, ##__VA_ARGS__)
+
+#define BLOG_LOG_CLIENT_CTX(ctx, client_id, fmt, ...) \
+	__BLOG_LOG_CTX(ctx, 0, client_id, fmt, ##__VA_ARGS__)
+
+#define __BLOG_LOG_CTX(__ctx, dbg, __client_id, fmt, ...) \
+	do { \
+		static u32 __source_id; \
+		static size_t __size; \
+		struct blog_tls_ctx *__tls; \
+		void *___buffer = NULL; \
+		(void)(dbg); \
+		if (unlikely(!(__ctx) || !(__ctx)->logger)) \
+			break; \
+		__tls = blog_get_ctx_ctx(__ctx); \
+		if (unlikely(!__tls)) \
+			break; \
+		if (unlikely(__source_id == 0)) { \
+			__source_id = blog_get_source_id_ctx(__ctx, \
+				kbasename(__FILE__), __func__, __LINE__, fmt); \
+			__size = blog_cnt(__VA_ARGS__); \
+		} \
+		___buffer = blog_log_with_ctx((__ctx)->logger, __tls, \
+					      __source_id, __client_id, __size); \
+		if (likely(___buffer)) { \
+			void *___tmp = ___buffer; \
+			if (__size > 0) \
+				blog_ser(___buffer, ##__VA_ARGS__); \
+			blog_log_commit_with_ctx((__ctx)->logger, __tls, \
+						 ___buffer - ___tmp); \
+		} \
+	} while (0)
+
+#endif /* _LINUX_CEPH_BLOG_MODULE_H */
diff --git a/include/linux/ceph/blog_pagefrag.h b/include/linux/ceph/blog_pagefrag.h
new file mode 100644
index 000000000000..b48095d9690c
--- /dev/null
+++ b/include/linux/ceph/blog_pagefrag.h
@@ -0,0 +1,32 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Binary Logging Page Fragment Management
+ */
+#ifndef _LINUX_CEPH_BLOG_PAGEFRAG_H
+#define _LINUX_CEPH_BLOG_PAGEFRAG_H
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/spinlock.h>
+
+#define BLOG_PAGEFRAG_SIZE  (1<<17)  /* 128KB */
+#define BLOG_PAGEFRAG_MASK (BLOG_PAGEFRAG_SIZE - 1)
+
+struct blog_pagefrag {
+	struct page *pages;
+	void *buffer;
+	size_t capacity;
+	spinlock_t lock;
+	unsigned int head;
+	unsigned int alloc_count;
+	int active_elements;
+	void *last_entry;
+};
+
+int blog_pagefrag_init_with_buffer(struct blog_pagefrag *pf, void *buffer, size_t size);
+int blog_pagefrag_reserve(struct blog_pagefrag *pf, unsigned int n);
+void blog_pagefrag_publish(struct blog_pagefrag *pf, u64 publish_head);
+void blog_pagefrag_reset(struct blog_pagefrag *pf);
+void *blog_pagefrag_get_ptr(struct blog_pagefrag *pf, u64 val);
+
+#endif /* _LINUX_CEPH_BLOG_PAGEFRAG_H */
diff --git a/include/linux/ceph/blog_ser.h b/include/linux/ceph/blog_ser.h
new file mode 100644
index 000000000000..1d570388ff09
--- /dev/null
+++ b/include/linux/ceph/blog_ser.h
@@ -0,0 +1,270 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Binary Logging Serialization
+ */
+#ifndef _LINUX_CEPH_BLOG_SER_H
+#define _LINUX_CEPH_BLOG_SER_H
+
+#include <linux/string.h>
+#include <linux/kernel.h>
+
+#define IS_CONST_STR_PTR(t) \
+	__builtin_types_compatible_p(typeof(t), const char *)
+
+#define IS_STR_PTR(t) \
+	__builtin_types_compatible_p(typeof(t), char *)
+
+#define IS_STR(t) \
+	(__builtin_types_compatible_p(typeof(t), const char *) || \
+	__builtin_types_compatible_p(typeof(t), char *))
+
+#define __suppress_cast_warning(type, value) \
+({ \
+	_Pragma("GCC diagnostic push") \
+	_Pragma("GCC diagnostic ignored \"-Wint-to-pointer-cast\"") \
+	_Pragma("GCC diagnostic ignored \"-Wpointer-to-int-cast\"") \
+	type __scw_result; \
+	__scw_result = ((type)(value)); \
+	_Pragma("GCC diagnostic pop") \
+	__scw_result; \
+})
+
+#define ___blog_concat(__a, __b) __a ## __b
+#define ___blog_apply(__fn, __n) ___blog_concat(__fn, __n)
+
+#define ___blog_nth(_, __1, __2, __3, __4, __5, __6, __7, __8, __9, \
+	__10, __11, __12, __13, __14, __15, __16, __17, __18, __19, __20, \
+	__21, __22, __23, __24, __25, __26, __27, __28, __29, __30, __31, \
+	__32, __N, ...) __N
+#define ___blog_narg(...) ___blog_nth(_, ##__VA_ARGS__, \
+	32, 31, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, \
+	16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0)
+#define blog_narg(...) ___blog_narg(__VA_ARGS__)
+
+#define STR_MAX_SIZE 255
+#define __sizeof(x) \
+	(IS_STR(x) ? STR_MAX_SIZE : \
+	 (sizeof(x) < 4) ? 4 : sizeof(x))
+
+/* Size calculation macros */
+#define ___blog_cnt0()		(0)
+#define ___blog_cnt1(__t)		(__sizeof(__t))
+#define ___blog_cnt2(__t, __args...)	(___blog_cnt1(__args) + __sizeof(__t))
+#define ___blog_cnt3(__t, __args...)	(___blog_cnt2(__args) + __sizeof(__t))
+#define ___blog_cnt4(__t, __args...)	(___blog_cnt3(__args) + __sizeof(__t))
+#define ___blog_cnt5(__t, __args...)	(___blog_cnt4(__args) + __sizeof(__t))
+#define ___blog_cnt6(__t, __args...)	(___blog_cnt5(__args) + __sizeof(__t))
+#define ___blog_cnt7(__t, __args...)	(___blog_cnt6(__args) + __sizeof(__t))
+#define ___blog_cnt8(__t, __args...)	(___blog_cnt7(__args) + __sizeof(__t))
+#define ___blog_cnt9(__t, __args...)	(___blog_cnt8(__args) + __sizeof(__t))
+#define ___blog_cnt10(__t, __args...)	(___blog_cnt9(__args) + __sizeof(__t))
+#define ___blog_cnt11(__t, __args...)	(___blog_cnt10(__args) + __sizeof(__t))
+#define ___blog_cnt12(__t, __args...)	(___blog_cnt11(__args) + __sizeof(__t))
+#define ___blog_cnt13(__t, __args...)	(___blog_cnt12(__args) + __sizeof(__t))
+#define ___blog_cnt14(__t, __args...)	(___blog_cnt13(__args) + __sizeof(__t))
+#define ___blog_cnt15(__t, __args...)	(___blog_cnt14(__args) + __sizeof(__t))
+#define ___blog_cnt16(__t, __args...)	(___blog_cnt15(__args) + __sizeof(__t))
+#define ___blog_cnt17(__t, __args...)	(___blog_cnt16(__args) + __sizeof(__t))
+#define ___blog_cnt18(__t, __args...)	(___blog_cnt17(__args) + __sizeof(__t))
+#define ___blog_cnt19(__t, __args...)	(___blog_cnt18(__args) + __sizeof(__t))
+#define ___blog_cnt20(__t, __args...)	(___blog_cnt19(__args) + __sizeof(__t))
+#define ___blog_cnt21(__t, __args...)	(___blog_cnt20(__args) + __sizeof(__t))
+#define ___blog_cnt22(__t, __args...)	(___blog_cnt21(__args) + __sizeof(__t))
+#define ___blog_cnt23(__t, __args...)	(___blog_cnt22(__args) + __sizeof(__t))
+#define ___blog_cnt24(__t, __args...)	(___blog_cnt23(__args) + __sizeof(__t))
+#define ___blog_cnt25(__t, __args...)	(___blog_cnt24(__args) + __sizeof(__t))
+#define ___blog_cnt26(__t, __args...)	(___blog_cnt25(__args) + __sizeof(__t))
+#define ___blog_cnt27(__t, __args...)	(___blog_cnt26(__args) + __sizeof(__t))
+#define ___blog_cnt28(__t, __args...)	(___blog_cnt27(__args) + __sizeof(__t))
+#define ___blog_cnt29(__t, __args...)	(___blog_cnt28(__args) + __sizeof(__t))
+#define ___blog_cnt30(__t, __args...)	(___blog_cnt29(__args) + __sizeof(__t))
+#define ___blog_cnt31(__t, __args...)	(___blog_cnt30(__args) + __sizeof(__t))
+#define ___blog_cnt32(__t, __args...)	(___blog_cnt31(__args) + __sizeof(__t))
+#define blog_cnt(...)	 ___blog_apply(___blog_cnt, blog_narg(__VA_ARGS__))(__VA_ARGS__)
+
+#define IS_STR_ARRAY(t) \
+	__builtin_types_compatible_p(typeof(t), char [])
+
+#define IS_DYNAMIC_CHAR_PTR(t) \
+	(__builtin_classify_type((t)) == 14 && \
+	 __builtin_types_compatible_p(typeof(t), char *) && \
+	 !__builtin_constant_p((t)))
+
+#define IS_STATIC_CHAR_ARRAY(t) \
+	(__builtin_classify_type((t)) == 5 && \
+	 __builtin_types_compatible_p(typeof(t), char[]) && \
+	 __builtin_constant_p((t)))
+
+#define IS_DYNAMIC_CHAR_ARRAY(t) \
+	(__builtin_classify_type((t)) == 5 && \
+	 __builtin_types_compatible_p(typeof(t), char[]) && \
+	 !__builtin_constant_p((t)))
+
+#define char_ptr(str) __suppress_cast_warning(char *, (str))
+
+#ifndef _BLOG_SER_HELPERS_DEFINED
+#define _BLOG_SER_HELPERS_DEFINED
+
+union null_str_u {
+	char str[8];
+	unsigned long force_align;
+};
+
+static const union null_str_u null_str = {
+	.str = "(NULL) \0"
+};
+
+static inline size_t write_null_str(char *dst)
+{
+	*(union null_str_u *)dst = null_str;
+	static_assert(sizeof(null_str.str) == sizeof(unsigned long),
+		     "null_str.str size must match unsigned long for proper alignment");
+	return __builtin_strlen(null_str.str);
+}
+
+static inline size_t strscpy_n(char *dst, const char *src)
+{
+	size_t count = 0;
+
+	while (count < STR_MAX_SIZE - 1) {
+		dst[count] = src[count];
+		if (src[count] == '\0')
+			goto out;
+		count++;
+	}
+
+	dst[count] = '\0';
+	pr_warn("blog_ser: string truncated, exceeded max size %d\n", STR_MAX_SIZE);
+out:
+	return count + 1;
+}
+
+static inline ssize_t __strscpy(char *dst, const char *src)
+{
+	if (src != NULL)
+		return strscpy_n(dst, src);
+	return write_null_str(dst);
+}
+
+static inline void *strscpy_n_update(char *dst, const char *src, const char *file, int line)
+{
+	ssize_t ret = __strscpy(dst, src);
+
+	if (unlikely(ret <= 0 || ret >= STR_MAX_SIZE)) {
+		pr_err("blog_ser: string handling error ret=%zd at %s:%d :: dst='%s' src='%s'\n",
+		       ret, file, line, dst, src ? src : "(null)");
+		if (ret >= STR_MAX_SIZE) {
+			dst[STR_MAX_SIZE - 1] = '\0';
+			ret = STR_MAX_SIZE;
+		} else {
+			dst[0] = '\0';
+			ret = 1;
+		}
+	}
+	return dst + round_up(ret, 4);
+}
+
+#endif /* _BLOG_SER_HELPERS_DEFINED */
+
+#define __blog_ser_type(__buffer, __t)                          \
+	(__builtin_choose_expr(IS_STATIC_CHAR_ARRAY((__t)),               \
+		((__buffer) = (void *)strscpy_n_update((__buffer),            \
+						char_ptr(__t),                             \
+						kbasename(__FILE__),                      \
+						__LINE__)),                               \
+	__builtin_choose_expr(IS_STR((__t)),               \
+		((__buffer) = (void *)strscpy_n_update((__buffer),            \
+						char_ptr(__t),                             \
+						kbasename(__FILE__),                      \
+						__LINE__)),                               \
+	__builtin_choose_expr(IS_STR_ARRAY((__t)),               \
+		 ((__buffer) = (void *)strscpy_n_update((__buffer),        \
+						char_ptr(__t),                       \
+						kbasename(__FILE__),                \
+						__LINE__)),                          \
+	__builtin_choose_expr(sizeof((__t)) == 1,                         \
+		(*(u32 *)(__buffer) = __suppress_cast_warning(u32, (__t)), \
+		 (__buffer) = (void *)((char *)(__buffer) + 4)),            \
+	__builtin_choose_expr(sizeof((__t)) == 2,                         \
+		(*(u32 *)(__buffer) = __suppress_cast_warning(u32, (__t)), \
+		 (__buffer) = (void *)((char *)(__buffer) + 4)),            \
+	__builtin_choose_expr(sizeof((__t)) == 4,                         \
+		(*(u32 *)(__buffer) = __suppress_cast_warning(u32, (__t)), \
+		 (__buffer) = (void *)((char *)(__buffer) + 4)),            \
+	__builtin_choose_expr(sizeof((__t)) == 8,                         \
+		(*(u64 *)(__buffer) = __suppress_cast_warning(u64, (__t)), \
+		 (__buffer) = (void *)((char *)(__buffer) + 8)),            \
+		(pr_err("UNSUPPORTED_TYPE: %s:%d: unsupported type size %zu\n",       \
+			kbasename(__FILE__), __LINE__, sizeof(__t)))              \
+	))))))))
+
+/* Serialization macros */
+#define ___blog_ser0(__buffer)
+#define ___blog_ser1(__buffer, __t)           (__blog_ser_type(__buffer, __t))
+#define ___blog_ser2(__buffer, __t, __args...)    \
+	(__blog_ser_type(__buffer, __t), ___blog_ser1(__buffer, __args))
+#define ___blog_ser3(__buffer, __t, __args...)    \
+	(__blog_ser_type(__buffer, __t), ___blog_ser2(__buffer, __args))
+#define ___blog_ser4(__buffer, __t, __args...)    \
+	(__blog_ser_type(__buffer, __t), ___blog_ser3(__buffer, __args))
+#define ___blog_ser5(__buffer, __t, __args...)    \
+	(__blog_ser_type(__buffer, __t), ___blog_ser4(__buffer, __args))
+#define ___blog_ser6(__buffer, __t, __args...)    \
+	(__blog_ser_type(__buffer, __t), ___blog_ser5(__buffer, __args))
+#define ___blog_ser7(__buffer, __t, __args...)    \
+	(__blog_ser_type(__buffer, __t), ___blog_ser6(__buffer, __args))
+#define ___blog_ser8(__buffer, __t, __args...)    \
+	(__blog_ser_type(__buffer, __t), ___blog_ser7(__buffer, __args))
+#define ___blog_ser9(__buffer, __t, __args...)    \
+	(__blog_ser_type(__buffer, __t), ___blog_ser8(__buffer, __args))
+#define ___blog_ser10(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser9(__buffer, __args))
+#define ___blog_ser11(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser10(__buffer, __args))
+#define ___blog_ser12(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser11(__buffer, __args))
+#define ___blog_ser13(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser12(__buffer, __args))
+#define ___blog_ser14(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser13(__buffer, __args))
+#define ___blog_ser15(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser14(__buffer, __args))
+#define ___blog_ser16(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser15(__buffer, __args))
+#define ___blog_ser17(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser16(__buffer, __args))
+#define ___blog_ser18(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser17(__buffer, __args))
+#define ___blog_ser19(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser18(__buffer, __args))
+#define ___blog_ser20(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser19(__buffer, __args))
+#define ___blog_ser21(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser20(__buffer, __args))
+#define ___blog_ser22(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser21(__buffer, __args))
+#define ___blog_ser23(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser22(__buffer, __args))
+#define ___blog_ser24(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser23(__buffer, __args))
+#define ___blog_ser25(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser24(__buffer, __args))
+#define ___blog_ser26(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser25(__buffer, __args))
+#define ___blog_ser27(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser26(__buffer, __args))
+#define ___blog_ser28(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser27(__buffer, __args))
+#define ___blog_ser29(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser28(__buffer, __args))
+#define ___blog_ser30(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser29(__buffer, __args))
+#define ___blog_ser31(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser30(__buffer, __args))
+#define ___blog_ser32(__buffer, __t, __args...)   \
+	(__blog_ser_type(__buffer, __t), ___blog_ser31(__buffer, __args))
+#define ___blog_ser(__buffer, ...)   \
+	___blog_apply(___blog_ser, blog_narg(__VA_ARGS__))(__buffer, ##__VA_ARGS__)
+#define blog_ser(...)  ___blog_ser(__VA_ARGS__)
+
+#endif /* _LINUX_CEPH_BLOG_SER_H */
-- 
2.34.1