From: Alex Markuze Date: Wed, 13 Aug 2025 14:41:15 +0000 (+0000) Subject: phase I X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=4dbfb9232bb3bff162418ee08fe5379af0bcab48;p=ceph-client.git phase I --- diff --git a/include/linux/blog/blog.h b/include/linux/blog/blog.h new file mode 100644 index 000000000000..da61987e0447 --- /dev/null +++ b/include/linux/blog/blog.h @@ -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 +#include +#include +#include +#include +#include +#include +#include + +/* 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 index 000000000000..ebe9004af68b --- /dev/null +++ b/include/linux/blog/blog_batch.h @@ -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 +#include +#include +#include + +/* 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 index 000000000000..b216f4d7ac0b --- /dev/null +++ b/include/linux/blog/blog_des.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Binary Logging Deserialization + */ +#ifndef _LINUX_BLOG_DES_H +#define _LINUX_BLOG_DES_H + +#include /* 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 index 000000000000..8f8bbf0edd61 --- /dev/null +++ b/include/linux/blog/blog_pagefrag.h @@ -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 +#include +#include + +#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 index 000000000000..f2b0a69f1603 --- /dev/null +++ b/include/linux/blog/blog_ser.h @@ -0,0 +1,227 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Binary Logging Serialization + */ +#ifndef _LINUX_BLOG_SER_H +#define _LINUX_BLOG_SER_H + +#include +#include + +#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 */ diff --git a/lib/Kconfig b/lib/Kconfig index 6c1b8f184267..e05f33f16015 100644 --- a/lib/Kconfig +++ b/lib/Kconfig @@ -599,6 +599,8 @@ source "lib/vdso/Kconfig" source "lib/fonts/Kconfig" +source "lib/blog/Kconfig" + config SG_SPLIT def_bool n help diff --git a/lib/Makefile b/lib/Makefile index f07b24ce1b3f..ea4fa4558736 100644 --- a/lib/Makefile +++ b/lib/Makefile @@ -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 index 000000000000..9681c6705ddc --- /dev/null +++ b/lib/blog/Kconfig @@ -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 index 000000000000..7010fb56372b --- /dev/null +++ b/lib/blog/Makefile @@ -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 index 000000000000..3fda2ba30aea --- /dev/null +++ b/lib/blog/blog_batch.c @@ -0,0 +1,56 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Binary Logging Batch Management - Stub Implementation + */ + +#include +#include +#include + +/** + * 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 index 000000000000..e89db113ef85 --- /dev/null +++ b/lib/blog/blog_core.c @@ -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 +#include +#include +#include +#include + +/* 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 index 000000000000..3ff84d53d388 --- /dev/null +++ b/lib/blog/blog_des.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Binary Logging Deserialization - Stub Implementation + */ + +#include +#include +#include + +/** + * 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 index 000000000000..61622dcb4f87 --- /dev/null +++ b/lib/blog/blog_pagefrag.c @@ -0,0 +1,104 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Binary Logging Page Fragment Management - Stub Implementation + */ + +#include +#include +#include + +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);