--- /dev/null
+/* 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_BLOG_H
+#define _LINUX_BLOG_H
+
+#include <linux/types.h>
+#include <linux/sched.h>
+#include <linux/list.h>
+#include <linux/spinlock.h>
+#include <linux/blog/blog_batch.h>
+#include <linux/blog/blog_pagefrag.h>
+#include <linux/blog/blog_ser.h>
+#include <linux/blog/blog_des.h>
+
+/* Debug configuration */
+#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
+
+/* Debug poison values */
+#if BLOG_DEBUG_POISON
+#define BLOG_LOG_ENTRY_POISON 0xD1E7C0DE
+#define BLOG_CTX_POISON 0xCAFEBABE
+#endif
+
+/* Global logger instance */
+extern struct blog_logger g_blog_logger;
+
+/* Maximum values */
+#define BLOG_LOG_MAX_LEN 256
+#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
+
+/* Source information mapping structure - preserves all ceph_san fields */
+struct blog_source_info {
+ const char *file;
+ const char *func;
+ unsigned int line;
+ const char *fmt; /* Format string */
+ int warn_count;
+#if BLOG_TRACK_USAGE
+ atomic_t napi_usage; /* Number of times used in NAPI context */
+ atomic_t task_usage; /* Number of times used in task context */
+ atomic_t napi_bytes; /* Total bytes used in NAPI context */
+ atomic_t task_bytes; /* Total bytes used in task context */
+#endif
+};
+
+/* Log entry structure - preserves all ceph_san fields */
+struct blog_log_entry {
+#if BLOG_DEBUG_POISON
+ u64 debug_poison; /* Debug poison value */
+#endif
+ u32 ts_delta; /* Time delta from base_jiffies */
+ u16 source_id; /* Source ID */
+ u8 client_id; /* Client ID (module-specific) */
+ u8 len; /* Length of buffer */
+ char buffer[]; /* Variable length buffer */
+};
+
+/* TLS context structure - preserves all ceph_san fields */
+struct blog_tls_ctx {
+ struct list_head list; /* List entry for global list */
+ struct blog_pagefrag pf; /* Page fragment for this context */
+ void (*release)(void *); /* Release function */
+ atomic_t refcount; /* Reference count */
+ struct task_struct *task; /* Associated task */
+ pid_t pid; /* Process ID */
+ char comm[TASK_COMM_LEN]; /* Command name */
+ u64 id; /* Unique context ID */
+ u64 debug_poison; /* Debug poison value */
+ unsigned long base_jiffies; /* Base jiffies value for this context */
+};
+
+/* Global logger state - preserves all ceph_san fields */
+struct blog_logger {
+ struct list_head contexts; /* List of all TLS contexts */
+ spinlock_t lock; /* Protects contexts list */
+ struct blog_batch alloc_batch; /* Batch for allocating new entries */
+ struct blog_batch log_batch; /* Batch for storing log entries */
+ struct blog_source_info source_map[BLOG_MAX_SOURCE_IDS]; /* Source info mapping */
+ atomic_t next_source_id; /* Next source ID to assign */
+ spinlock_t source_lock; /* Protects source operations */
+ unsigned long total_contexts_allocated;
+ u64 next_ctx_id; /* Next context ID to assign */
+ spinlock_t ctx_id_lock; /* Protects context ID counter */
+ struct blog_tls_ctx __percpu *napi_ctxs; /* Per-CPU NAPI contexts */
+};
+
+/* Iterator for log entries in a single pagefrag */
+struct blog_log_iter {
+ struct blog_pagefrag *pf; /* Pagefrag being iterated */
+ u64 current_offset; /* Current offset in pagefrag */
+ u64 end_offset; /* End offset in pagefrag */
+ u64 prev_offset; /* Previous offset for debugging */
+ u64 steps; /* Number of steps taken */
+};
+
+/* Client deserialization callback type */
+typedef int (*blog_client_des_fn)(char *buf, size_t size, u8 client_id);
+
+/* Core API functions */
+
+/* Initialize the logging system */
+int blog_init(void);
+
+/* Clean up the logging system */
+void blog_cleanup(void);
+
+/* Get or create source ID */
+u32 blog_get_source_id(const char *file, const char *func, unsigned int line, const char *fmt);
+
+/* Get source information for ID */
+struct blog_source_info *blog_get_source_info(u32 id);
+
+/* Log a message - returns buffer to write to */
+void* blog_log(u32 source_id, u8 client_id, size_t needed_size);
+
+/* Get current TLS context, creating if necessary */
+struct blog_tls_ctx *blog_get_tls_ctx(void);
+
+/* Get NAPI context for current CPU */
+struct blog_tls_ctx *blog_get_napi_ctx(void);
+
+/* Set NAPI context for current CPU */
+void blog_set_napi_ctx(struct blog_tls_ctx *ctx);
+
+/* Get appropriate context based on context type */
+struct blog_tls_ctx *blog_get_ctx(void);
+
+/* Trim the current context's pagefrag by n bytes */
+int blog_log_trim(unsigned int n);
+
+/* Initialize the iterator for a specific pagefrag */
+void blog_log_iter_init(struct blog_log_iter *iter, struct blog_pagefrag *pf);
+
+/* Get next log entry, returns NULL when no more entries */
+struct blog_log_entry *blog_log_iter_next(struct blog_log_iter *iter);
+
+/* Deserialization with callback */
+int blog_des_entry(struct blog_log_entry *entry, char *output, size_t out_size,
+ blog_client_des_fn client_cb);
+
+/* Helper functions */
+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);
+}
+
+/* Check if address is in valid kernel address range */
+bool blog_is_valid_kernel_addr(const void *addr);
+
+/* Helper macro for logging */
+#define __BLOG_LOG(dbg, __client_id, fmt, ...) \
+ do { \
+ static u32 __source_id = 0; \
+ static size_t __size = 0; \
+ void *___buffer = NULL; \
+ if (unlikely(__source_id == 0)) { \
+ __source_id = blog_get_source_id(kbasename(__FILE__), __func__, __LINE__, fmt); \
+ __size = blog_cnt(__VA_ARGS__); \
+ } \
+ ___buffer = blog_log(__source_id, __client_id, __size); \
+ if (likely(___buffer) && __size > 0) { \
+ void *___tmp = ___buffer; \
+ size_t actual_size; \
+ blog_ser(___buffer, ##__VA_ARGS__);\
+ actual_size = ___buffer - ___tmp; \
+ blog_log_trim(__size - actual_size); \
+ } \
+ } while (0)
+
+#define BLOG_LOG(fmt, ...) \
+ __BLOG_LOG(0, 0, fmt, ##__VA_ARGS__)
+
+/* Helper macro for logging with client ID */
+#define BLOG_LOG_CLIENT(client_id, fmt, ...) \
+ __BLOG_LOG(0, client_id, fmt, ##__VA_ARGS__)
+
+#endif /* _LINUX_BLOG_H */
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Binary Logging Batch Management
+ */
+#ifndef _LINUX_BLOG_BATCH_H
+#define _LINUX_BLOG_BATCH_H
+
+#include <linux/types.h>
+#include <linux/percpu.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+
+/* Size of each magazine (number of elements it can hold) */
+#define BLOG_MAGAZINE_SIZE 16
+
+/* Structure representing a single magazine */
+struct blog_magazine {
+ struct list_head list; /* For linking in global pools */
+ unsigned int count; /* Number of elements currently in magazine */
+ void *elements[BLOG_MAGAZINE_SIZE];
+};
+
+/* Per-CPU magazine state */
+struct blog_cpu_magazine {
+ struct blog_magazine *mag; /* Current magazine for this CPU */
+};
+
+/* Global magazine pools */
+struct blog_batch {
+ struct list_head full_magazines; /* List of full magazines */
+ struct list_head empty_magazines; /* List of empty magazines */
+ spinlock_t full_lock; /* Protects full magazine list and count */
+ spinlock_t empty_lock; /* Protects empty magazine list and count */
+ unsigned int nr_full; /* Protected by full_lock */
+ unsigned int nr_empty; /* Protected by empty_lock */
+ struct blog_cpu_magazine __percpu *cpu_magazines; /* Per-CPU magazines */
+ struct kmem_cache *magazine_cache; /* Cache for magazine allocations */
+};
+
+/* Initialize the batching system */
+int blog_batch_init(struct blog_batch *batch);
+
+/* Clean up the batching system */
+void blog_batch_cleanup(struct blog_batch *batch);
+
+/* Get an element from the batch */
+void *blog_batch_get(struct blog_batch *batch);
+
+/* Put an element back into the batch */
+void blog_batch_put(struct blog_batch *batch, void *element);
+
+#endif /* _LINUX_BLOG_BATCH_H */
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Binary Logging Deserialization
+ */
+#ifndef _LINUX_BLOG_DES_H
+#define _LINUX_BLOG_DES_H
+
+#include <linux/types.h> /* For size_t */
+
+/* Forward declaration */
+struct blog_log_entry;
+
+/**
+ * blog_des_reconstruct - Reconstructs a formatted string from serialized values
+ * @fmt: Format string containing % specifiers
+ * @buffer: Buffer containing serialized values
+ * @nr_args: Number of arguments to process
+ * @size: Size of the buffer in bytes
+ * @out: Buffer to store the reconstructed string
+ * @out_size: Size of the output buffer
+ *
+ * The function uses the format string to determine the types and number of values
+ * to extract from the buffer.
+ *
+ * Return: Number of bytes written to out buffer, or negative error code on failure
+ */
+int blog_des_reconstruct(const char *fmt, const void *buffer, size_t nr_args,
+ size_t size, char *out, size_t out_size);
+
+/**
+ * blog_log_reconstruct - Reconstructs a formatted string from a log entry
+ * @entry: Log entry containing serialized data
+ * @output: Buffer to write the formatted string to
+ * @output_size: Size of the output buffer
+ *
+ * This is a wrapper around blog_des_reconstruct that handles log entry parsing.
+ * Note: This does NOT handle client_id - the caller should handle that separately
+ * using their module-specific callback.
+ *
+ * Return: Length of formatted string, or negative error code on failure
+ */
+int blog_log_reconstruct(const struct blog_log_entry *entry, char *output, size_t output_size);
+
+#endif /* _LINUX_BLOG_DES_H */
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Binary Logging Page Fragment Management
+ */
+#ifndef _LINUX_BLOG_PAGEFRAG_H
+#define _LINUX_BLOG_PAGEFRAG_H
+
+#include <linux/types.h>
+#include <linux/mm.h>
+#include <linux/spinlock.h>
+
+#define BLOG_PAGEFRAG_SIZE (1<<19) /* 512KB */
+#define BLOG_PAGEFRAG_MASK (BLOG_PAGEFRAG_SIZE - 1)
+
+/* Pagefrag allocator structure */
+struct blog_pagefrag {
+ struct page *pages;
+ void *buffer;
+ spinlock_t lock; /* protects head */
+ unsigned int head;
+ unsigned int alloc_count;
+ int active_elements;
+ void *last_entry; /* Pointer to the last allocated entry */
+};
+
+int blog_pagefrag_init(struct blog_pagefrag *pf);
+int blog_pagefrag_init_with_buffer(struct blog_pagefrag *pf, void *buffer, size_t size);
+int blog_pagefrag_alloc(struct blog_pagefrag *pf, unsigned int n);
+void *blog_pagefrag_get_ptr_from_tail(struct blog_pagefrag *pf);
+void blog_pagefrag_free(struct blog_pagefrag *pf, unsigned int n);
+void blog_pagefrag_deinit(struct blog_pagefrag *pf);
+void blog_pagefrag_reset(struct blog_pagefrag *pf);
+void *blog_pagefrag_get_ptr(struct blog_pagefrag *pf, u64 val);
+bool blog_pagefrag_is_wraparound(u64 val);
+
+/* Get allocation size from pagefrag allocation result */
+u64 blog_pagefrag_get_alloc_size(u64 val);
+
+#define BLOG_PAGEFRAG_GET_N(val) ((val) >> 32)
+
+void blog_pagefrag_trim_head(struct blog_pagefrag *pf, unsigned int n);
+void blog_pagefrag_trim(struct blog_pagefrag *pf, unsigned int n);
+
+#endif /* _LINUX_BLOG_PAGEFRAG_H */
--- /dev/null
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Binary Logging Serialization
+ */
+#ifndef _LINUX_BLOG_SER_H
+#define _LINUX_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 256
+#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))
+
+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)");
+ /* Return safely instead of panicking - truncate and continue */
+ if (ret >= STR_MAX_SIZE) {
+ dst[STR_MAX_SIZE - 1] = '\0';
+ ret = STR_MAX_SIZE;
+ } else {
+ /* Handle null or empty string case */
+ dst[0] = '\0';
+ ret = 1;
+ }
+ }
+ return dst + round_up(ret, 4);
+}
+
+/* Serialization type macro */
+#define __blog_ser_type(__buffer, __t) \
+ (__builtin_choose_expr((IS_DYNAMIC_CHAR_PTR((__t)) || IS_STATIC_CHAR_ARRAY((__t))), \
+ /* For static arrays (like __func__), just save pointer */ \
+ (pr_err("DYNAMIC_PTR: %s:%d: saving pointer %llx\n", kbasename(__FILE__), __LINE__, (unsigned long long)(__t)), \
+ *(void **)(__buffer) = __suppress_cast_warning(void *, (__t)), \
+ (__buffer) = (void *)((char *)(__buffer) + sizeof(void *))), \
+ __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)), \
+ /* For dynamic arrays, save NULL and string bytes */ \
+ ((__buffer) = (void *)strscpy_n_update((__buffer), char_ptr(__t), kbasename(__FILE__), __LINE__)), \
+ __builtin_choose_expr(sizeof((__t)) == 1, \
+ (*(uint32_t *)(__buffer) = __suppress_cast_warning(uint32_t, (__t)), \
+ (__buffer) = (void *)((char *)(__buffer) + 4)), \
+ __builtin_choose_expr(sizeof((__t)) == 2, /* we have no way to differentiate u16 and u32 in deserialization */ \
+ (*(uint32_t *)(__buffer) = __suppress_cast_warning(uint32_t, (__t)), \
+ (__buffer) = (void *)((char *)(__buffer) + 4)), \
+ __builtin_choose_expr(sizeof((__t)) == 4, \
+ (*(uint32_t *)(__buffer) = __suppress_cast_warning(uint32_t, (__t)), \
+ (__buffer) = (void *)((char *)(__buffer) + 4)), \
+ __builtin_choose_expr(sizeof((__t)) == 8, \
+ (*(uint64_t *)(__buffer) = __suppress_cast_warning(uint64_t, (__t)), \
+ (__buffer) = (void *)((char *)(__buffer) + 8)), \
+ (pr_err("UNSUPPORTED_TYPE: %s:%d: unsupported type size %s\n", kbasename(__FILE__), __LINE__, #__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_BLOG_SER_H */
source "lib/fonts/Kconfig"
+source "lib/blog/Kconfig"
+
config SG_SPLIT
def_bool n
help
obj-$(CONFIG_FONT_SUPPORT) += fonts/
+obj-$(CONFIG_BLOG) += blog/
+
hostprogs := gen_crc32table
hostprogs += gen_crc64table
clean-files := crc32table.h
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0-only
+#
+# Binary Logging Infrastructure (BLOG)
+#
+
+config BLOG
+ tristate "Binary Logging Infrastructure"
+ help
+ Generic binary logging infrastructure for kernel subsystems.
+ Provides efficient batched logging with binary serialization
+ and deserialization support. Modules using BLOG maintain their
+ own client mappings and debugfs interfaces.
+
+ If unsure, say N.
+
+config BLOG_DEBUG
+ bool "Binary Logging Debug Support"
+ depends on BLOG
+ default n
+ help
+ Enable debug features for the binary logging infrastructure,
+ including memory poisoning, validation checks, and usage tracking.
+ This adds overhead and should only be enabled for debugging.
+
+ If unsure, say N.
+
+config BLOG_MAX_CLIENTS
+ int "Maximum number of logging clients"
+ depends on BLOG
+ range 16 1024
+ default 256
+ help
+ Maximum number of client IDs that can be used by modules.
+ Each module using BLOG can register multiple clients up to
+ this limit. The client ID is stored as u8, so maximum is 256.
+
+config BLOG_MAX_SOURCES
+ int "Maximum number of source locations"
+ depends on BLOG
+ range 256 16384
+ default 4096
+ help
+ Maximum number of unique source code locations (file/function/line)
+ that can be tracked. Each unique logging call site gets a source ID.
+
+config BLOG_TRACK_USAGE
+ bool "Track usage statistics"
+ depends on BLOG_DEBUG
+ default n
+ help
+ Track usage statistics for logging operations, including counts
+ and bytes used in different contexts (task vs NAPI).
+
+ If unsure, say N.
--- /dev/null
+# SPDX-License-Identifier: GPL-2.0
+#
+# Makefile for Binary Logging Infrastructure (BLOG)
+#
+
+obj-$(CONFIG_BLOG) += blog.o
+
+blog-y := blog_core.o blog_batch.o blog_pagefrag.o blog_des.o
+
+# Debug support
+# blog-$(CONFIG_BLOG_DEBUG) += blog_debug.o
+
+# Compiler flags
+ccflags-$(CONFIG_BLOG_DEBUG) += -DBLOG_DEBUG=1
+ccflags-$(CONFIG_BLOG_TRACK_USAGE) += -DBLOG_TRACK_USAGE=1
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Binary Logging Batch Management - Stub Implementation
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/blog/blog_batch.h>
+
+/**
+ * blog_batch_init - Initialize the batching system
+ */
+int blog_batch_init(struct blog_batch *batch)
+{
+ /* Stub implementation */
+ if (!batch)
+ return -EINVAL;
+
+ INIT_LIST_HEAD(&batch->full_magazines);
+ INIT_LIST_HEAD(&batch->empty_magazines);
+ spin_lock_init(&batch->full_lock);
+ spin_lock_init(&batch->empty_lock);
+ batch->nr_full = 0;
+ batch->nr_empty = 0;
+
+ return 0;
+}
+EXPORT_SYMBOL(blog_batch_init);
+
+/**
+ * blog_batch_cleanup - Clean up the batching system
+ */
+void blog_batch_cleanup(struct blog_batch *batch)
+{
+ /* Stub implementation */
+}
+EXPORT_SYMBOL(blog_batch_cleanup);
+
+/**
+ * blog_batch_get - Get an element from the batch
+ */
+void *blog_batch_get(struct blog_batch *batch)
+{
+ /* Stub implementation */
+ return NULL;
+}
+EXPORT_SYMBOL(blog_batch_get);
+
+/**
+ * blog_batch_put - Put an element back into the batch
+ */
+void blog_batch_put(struct blog_batch *batch, void *element)
+{
+ /* Stub implementation */
+}
+EXPORT_SYMBOL(blog_batch_put);
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Binary Logging Infrastructure - Core Implementation
+ *
+ * This is a stub implementation for Phase 1 infrastructure setup.
+ * Full implementation will be added in Phase 2.
+ */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/blog/blog.h>
+
+/* Global logger instance */
+struct blog_logger g_blog_logger;
+EXPORT_SYMBOL(g_blog_logger);
+
+/**
+ * blog_init - Initialize the logging system
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int blog_init(void)
+{
+ pr_info("BLOG: Binary logging infrastructure initialized (stub)\n");
+ return 0;
+}
+EXPORT_SYMBOL(blog_init);
+
+/**
+ * blog_cleanup - Clean up the logging system
+ */
+void blog_cleanup(void)
+{
+ pr_info("BLOG: Binary logging infrastructure cleanup (stub)\n");
+}
+EXPORT_SYMBOL(blog_cleanup);
+
+/**
+ * blog_get_source_id - Get or create source ID
+ */
+u32 blog_get_source_id(const char *file, const char *func, unsigned int line, const char *fmt)
+{
+ /* Stub implementation */
+ return 0;
+}
+EXPORT_SYMBOL(blog_get_source_id);
+
+/**
+ * blog_get_source_info - Get source information for ID
+ */
+struct blog_source_info *blog_get_source_info(u32 id)
+{
+ /* Stub implementation */
+ return NULL;
+}
+EXPORT_SYMBOL(blog_get_source_info);
+
+/**
+ * blog_log - Log a message
+ */
+void* blog_log(u32 source_id, u8 client_id, size_t needed_size)
+{
+ /* Stub implementation */
+ return NULL;
+}
+EXPORT_SYMBOL(blog_log);
+
+/**
+ * blog_get_tls_ctx - Get current TLS context
+ */
+struct blog_tls_ctx *blog_get_tls_ctx(void)
+{
+ /* Stub implementation */
+ return NULL;
+}
+EXPORT_SYMBOL(blog_get_tls_ctx);
+
+/**
+ * blog_get_napi_ctx - Get NAPI context for current CPU
+ */
+struct blog_tls_ctx *blog_get_napi_ctx(void)
+{
+ /* Stub implementation */
+ return NULL;
+}
+EXPORT_SYMBOL(blog_get_napi_ctx);
+
+/**
+ * blog_set_napi_ctx - Set NAPI context for current CPU
+ */
+void blog_set_napi_ctx(struct blog_tls_ctx *ctx)
+{
+ /* Stub implementation */
+}
+EXPORT_SYMBOL(blog_set_napi_ctx);
+
+/**
+ * blog_get_ctx - Get appropriate context based on context type
+ */
+struct blog_tls_ctx *blog_get_ctx(void)
+{
+ /* Stub implementation */
+ return NULL;
+}
+EXPORT_SYMBOL(blog_get_ctx);
+
+/**
+ * blog_log_trim - Trim the current context's pagefrag by n bytes
+ */
+int blog_log_trim(unsigned int n)
+{
+ /* Stub implementation */
+ return 0;
+}
+EXPORT_SYMBOL(blog_log_trim);
+
+/**
+ * blog_log_iter_init - Initialize the iterator for a specific pagefrag
+ */
+void blog_log_iter_init(struct blog_log_iter *iter, struct blog_pagefrag *pf)
+{
+ /* Stub implementation */
+}
+EXPORT_SYMBOL(blog_log_iter_init);
+
+/**
+ * blog_log_iter_next - Get next log entry
+ */
+struct blog_log_entry *blog_log_iter_next(struct blog_log_iter *iter)
+{
+ /* Stub implementation */
+ return NULL;
+}
+EXPORT_SYMBOL(blog_log_iter_next);
+
+/**
+ * blog_des_entry - Deserialize entry with callback
+ */
+int blog_des_entry(struct blog_log_entry *entry, char *output, size_t out_size,
+ blog_client_des_fn client_cb)
+{
+ /* Stub implementation */
+ return 0;
+}
+EXPORT_SYMBOL(blog_des_entry);
+
+/**
+ * blog_is_valid_kernel_addr - Check if address is valid
+ */
+bool blog_is_valid_kernel_addr(const void *addr)
+{
+ /* Stub implementation */
+ return virt_addr_valid(addr);
+}
+EXPORT_SYMBOL(blog_is_valid_kernel_addr);
+
+static int __init blog_module_init(void)
+{
+ return blog_init();
+}
+
+static void __exit blog_module_exit(void)
+{
+ blog_cleanup();
+}
+
+module_init(blog_module_init);
+module_exit(blog_module_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Binary Logging Infrastructure");
+MODULE_AUTHOR("Linux Kernel Community");
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Binary Logging Deserialization - Stub Implementation
+ */
+
+#include <linux/module.h>
+#include <linux/blog/blog.h>
+#include <linux/blog/blog_des.h>
+
+/**
+ * blog_des_reconstruct - Reconstructs a formatted string from serialized values
+ */
+int blog_des_reconstruct(const char *fmt, const void *buffer, size_t nr_args,
+ size_t size, char *out, size_t out_size)
+{
+ /* Stub implementation */
+ if (!fmt || !buffer || !out)
+ return -EINVAL;
+
+ /* For now, just return a placeholder string */
+ return snprintf(out, out_size, "[BLOG stub: fmt=%s]", fmt);
+}
+EXPORT_SYMBOL(blog_des_reconstruct);
+
+/**
+ * blog_log_reconstruct - Reconstructs a formatted string from a log entry
+ */
+int blog_log_reconstruct(const struct blog_log_entry *entry, char *output, size_t output_size)
+{
+ /* Stub implementation */
+ if (!entry || !output)
+ return -EINVAL;
+
+ /* For now, just return a placeholder string */
+ return snprintf(output, output_size, "[BLOG entry stub]");
+}
+EXPORT_SYMBOL(blog_log_reconstruct);
--- /dev/null
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Binary Logging Page Fragment Management - Stub Implementation
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/blog/blog_pagefrag.h>
+
+int blog_pagefrag_init(struct blog_pagefrag *pf)
+{
+ /* Stub implementation */
+ if (!pf)
+ return -EINVAL;
+
+ memset(pf, 0, sizeof(*pf));
+ spin_lock_init(&pf->lock);
+ return 0;
+}
+EXPORT_SYMBOL(blog_pagefrag_init);
+
+int blog_pagefrag_init_with_buffer(struct blog_pagefrag *pf, void *buffer, size_t size)
+{
+ /* Stub implementation */
+ if (!pf || !buffer)
+ return -EINVAL;
+
+ memset(pf, 0, sizeof(*pf));
+ spin_lock_init(&pf->lock);
+ pf->buffer = buffer;
+ return 0;
+}
+EXPORT_SYMBOL(blog_pagefrag_init_with_buffer);
+
+int blog_pagefrag_alloc(struct blog_pagefrag *pf, unsigned int n)
+{
+ /* Stub implementation */
+ return 0;
+}
+EXPORT_SYMBOL(blog_pagefrag_alloc);
+
+void *blog_pagefrag_get_ptr_from_tail(struct blog_pagefrag *pf)
+{
+ /* Stub implementation */
+ return NULL;
+}
+EXPORT_SYMBOL(blog_pagefrag_get_ptr_from_tail);
+
+void blog_pagefrag_free(struct blog_pagefrag *pf, unsigned int n)
+{
+ /* Stub implementation */
+}
+EXPORT_SYMBOL(blog_pagefrag_free);
+
+void blog_pagefrag_deinit(struct blog_pagefrag *pf)
+{
+ /* Stub implementation */
+}
+EXPORT_SYMBOL(blog_pagefrag_deinit);
+
+void blog_pagefrag_reset(struct blog_pagefrag *pf)
+{
+ /* Stub implementation */
+ if (pf) {
+ pf->head = 0;
+ pf->alloc_count = 0;
+ pf->active_elements = 0;
+ pf->last_entry = NULL;
+ }
+}
+EXPORT_SYMBOL(blog_pagefrag_reset);
+
+void *blog_pagefrag_get_ptr(struct blog_pagefrag *pf, u64 val)
+{
+ /* Stub implementation */
+ return NULL;
+}
+EXPORT_SYMBOL(blog_pagefrag_get_ptr);
+
+bool blog_pagefrag_is_wraparound(u64 val)
+{
+ /* Stub implementation */
+ return false;
+}
+EXPORT_SYMBOL(blog_pagefrag_is_wraparound);
+
+u64 blog_pagefrag_get_alloc_size(u64 val)
+{
+ /* Stub implementation */
+ return 0;
+}
+EXPORT_SYMBOL(blog_pagefrag_get_alloc_size);
+
+void blog_pagefrag_trim_head(struct blog_pagefrag *pf, unsigned int n)
+{
+ /* Stub implementation */
+}
+EXPORT_SYMBOL(blog_pagefrag_trim_head);
+
+void blog_pagefrag_trim(struct blog_pagefrag *pf, unsigned int n)
+{
+ /* Stub implementation */
+}
+EXPORT_SYMBOL(blog_pagefrag_trim);