]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-client.git/commitdiff
phase I
authorAlex Markuze <amarkuze@redhat.com>
Wed, 13 Aug 2025 14:41:15 +0000 (14:41 +0000)
committerAlex Markuze <amarkuze@redhat.com>
Wed, 13 Aug 2025 14:41:15 +0000 (14:41 +0000)
13 files changed:
include/linux/blog/blog.h [new file with mode: 0644]
include/linux/blog/blog_batch.h [new file with mode: 0644]
include/linux/blog/blog_des.h [new file with mode: 0644]
include/linux/blog/blog_pagefrag.h [new file with mode: 0644]
include/linux/blog/blog_ser.h [new file with mode: 0644]
lib/Kconfig
lib/Makefile
lib/blog/Kconfig [new file with mode: 0644]
lib/blog/Makefile [new file with mode: 0644]
lib/blog/blog_batch.c [new file with mode: 0644]
lib/blog/blog_core.c [new file with mode: 0644]
lib/blog/blog_des.c [new file with mode: 0644]
lib/blog/blog_pagefrag.c [new file with mode: 0644]

diff --git a/include/linux/blog/blog.h b/include/linux/blog/blog.h
new file mode 100644 (file)
index 0000000..da61987
--- /dev/null
@@ -0,0 +1,204 @@
+/* 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 */
diff --git a/include/linux/blog/blog_batch.h b/include/linux/blog/blog_batch.h
new file mode 100644 (file)
index 0000000..ebe9004
--- /dev/null
@@ -0,0 +1,52 @@
+/* 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 */
diff --git a/include/linux/blog/blog_des.h b/include/linux/blog/blog_des.h
new file mode 100644 (file)
index 0000000..b216f4d
--- /dev/null
@@ -0,0 +1,44 @@
+/* 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 */
diff --git a/include/linux/blog/blog_pagefrag.h b/include/linux/blog/blog_pagefrag.h
new file mode 100644 (file)
index 0000000..8f8bbf0
--- /dev/null
@@ -0,0 +1,44 @@
+/* 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 */
diff --git a/include/linux/blog/blog_ser.h b/include/linux/blog/blog_ser.h
new file mode 100644 (file)
index 0000000..f2b0a69
--- /dev/null
@@ -0,0 +1,227 @@
+/* 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 */
index 6c1b8f1842678c13966768177781b2de04601bdd..e05f33f160151a1cbc349f3996079aa1c16a252b 100644 (file)
@@ -599,6 +599,8 @@ source "lib/vdso/Kconfig"
 
 source "lib/fonts/Kconfig"
 
+source "lib/blog/Kconfig"
+
 config SG_SPLIT
        def_bool n
        help
index f07b24ce1b3f8db28796e461db1324d97133fdd5..ea4fa455873608b9f713d89918e4c6f10708a165 100644 (file)
@@ -295,6 +295,8 @@ obj-$(CONFIG_ASN1_ENCODER) += asn1_encoder.o
 
 obj-$(CONFIG_FONT_SUPPORT) += fonts/
 
+obj-$(CONFIG_BLOG) += blog/
+
 hostprogs      := gen_crc32table
 hostprogs      += gen_crc64table
 clean-files    := crc32table.h
diff --git a/lib/blog/Kconfig b/lib/blog/Kconfig
new file mode 100644 (file)
index 0000000..9681c67
--- /dev/null
@@ -0,0 +1,54 @@
+# 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.
diff --git a/lib/blog/Makefile b/lib/blog/Makefile
new file mode 100644 (file)
index 0000000..7010fb5
--- /dev/null
@@ -0,0 +1,15 @@
+# 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
diff --git a/lib/blog/blog_batch.c b/lib/blog/blog_batch.c
new file mode 100644 (file)
index 0000000..3fda2ba
--- /dev/null
@@ -0,0 +1,56 @@
+// 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);
diff --git a/lib/blog/blog_core.c b/lib/blog/blog_core.c
new file mode 100644 (file)
index 0000000..e89db11
--- /dev/null
@@ -0,0 +1,174 @@
+// 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");
diff --git a/lib/blog/blog_des.c b/lib/blog/blog_des.c
new file mode 100644 (file)
index 0000000..3ff84d5
--- /dev/null
@@ -0,0 +1,37 @@
+// 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);
diff --git a/lib/blog/blog_pagefrag.c b/lib/blog/blog_pagefrag.c
new file mode 100644 (file)
index 0000000..61622dc
--- /dev/null
@@ -0,0 +1,104 @@
+// 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);