From 3d957afa4285ed4deaaf42d200ba7ee1f3092f8d Mon Sep 17 00:00:00 2001 From: Alex Markuze Date: Tue, 25 Mar 2025 15:00:44 +0000 Subject: [PATCH] ceph_san: binary logging --- fs/ceph/debugfs.c | 126 ++++++------ include/linux/ceph/ceph_san_logger.h | 48 ++++- include/linux/ceph/ceph_san_packer.h | 87 ++++++++ net/ceph/ceph_san_logger.c | 290 ++++++++++++++++++++------- 4 files changed, 404 insertions(+), 147 deletions(-) create mode 100644 include/linux/ceph/ceph_san_packer.h diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c index 3c0ea06c076b2..bc087146dd08b 100644 --- a/fs/ceph/debugfs.c +++ b/fs/ceph/debugfs.c @@ -31,6 +31,9 @@ #include "mds_client.h" #include "metric.h" +/* Export the g_registrations array for debugfs code */ +extern struct ceph_san_log_registration g_registrations[]; + static int mdsmap_show(struct seq_file *s, void *p) { int i; @@ -358,34 +361,15 @@ static int mds_sessions_show(struct seq_file *s, void *ptr) return 0; } -/* @buffer: The buffer to store the formatted date and time string. - * @buffer_len: The length of the buffer. - * - * Returns: The number of characters written to the buffer, or a negative error code. - */ -static int jiffies_to_formatted_time(unsigned long jiffies_value, char *buffer, size_t buffer_len) +/* Simple wrapper to format jiffies to a human-readable timestamp */ +int jiffies_to_formatted_time(unsigned long jiffies_value, char *buffer, size_t buffer_len) { - unsigned long seconds; + struct timespec64 ts; struct tm tm_time; - // Convert jiffies to seconds - seconds = jiffies_value / HZ; - - // Manually convert seconds to struct tm - // This is a simplified conversion without timezone handling - tm_time.tm_sec = seconds % 60; - seconds /= 60; - tm_time.tm_min = seconds % 60; - seconds /= 60; - tm_time.tm_hour = seconds % 24; - seconds /= 24; + jiffies_to_timespec64(jiffies_value, &ts); + time64_to_tm(ts.tv_sec, 0, &tm_time); - // Simplified date calculation (assuming Unix epoch start at 1970-01-01) - tm_time.tm_mday = (seconds % 365) + 1; // Simplified day of month - tm_time.tm_mon = (seconds / 365) % 12; // Simplified month - tm_time.tm_year = (seconds / 365 / 12) + 70; // Simplified year since 1970 - - // Format the time into the buffer return snprintf(buffer, buffer_len, "%04ld-%02d-%02d %02d:%02d:%02d", tm_time.tm_year + 1900, tm_time.tm_mon + 1, tm_time.tm_mday, tm_time.tm_hour, tm_time.tm_min, tm_time.tm_sec); @@ -406,50 +390,56 @@ static int status_show(struct seq_file *s, void *p) static int ceph_san_tls_show(struct seq_file *s, void *p) { - struct ceph_san_tls_ctx *ctx; - struct ceph_san_log_iter iter; - struct ceph_san_log_entry *entry; - - /* Lock the logger to safely traverse the contexts list */ - spin_lock(&g_logger.lock); - - list_for_each_entry(ctx, &g_logger.contexts, list) { - - /* Initialize iterator for this context's pagefrag */ - ceph_san_log_iter_init(&iter, &ctx->pf); - int pid = ctx->pid; - char *comm = ctx->comm; - - /* Lock the pagefrag before accessing entries */ - spin_lock_bh(&ctx->pf.lock); - - /* Iterate through all log entries in this context */ - while ((entry = ceph_san_log_iter_next(&iter)) != NULL) { - if (entry->debug_poison != CEPH_SAN_LOG_ENTRY_POISON) { - seq_puts(s, " [Corrupted Entry]\n"); - continue; - } - - char datetime_str[32]; - jiffies_to_formatted_time(entry->ts, datetime_str, sizeof(datetime_str)); - - seq_printf(s, "[%d][%s][%s]:%s:%s:%u: %.*s", - pid, - comm, - datetime_str, - entry->file, - entry->func, - entry->line, - entry->len, - entry->buffer); - } - - /* Unlock the pagefrag after we're done with this context */ - spin_unlock_bh(&ctx->pf.lock); - } - - spin_unlock(&g_logger.lock); - return 0; + struct ceph_san_tls_ctx *ctx; + struct ceph_san_log_iter iter; + struct ceph_san_log_entry *entry; + struct ceph_san_log_registration *reg; + char time_str[32]; + char cmdline[TASK_COMM_LEN]; + int count = 0; + + ctx = ceph_san_get_tls_ctx(); + if (!ctx) + return -ENOENT; + + get_task_comm(cmdline, current); + + seq_printf(s, "Thread: %s [%d]\n", cmdline, task_pid_nr(current)); + seq_printf(s, "---------------------------------------------------------\n"); + + ceph_san_log_iter_init(&iter, &ctx->pf); + + while ((entry = ceph_san_log_iter_next(&iter)) != NULL) { + if (entry->debug_poison != CEPH_SAN_LOG_ENTRY_POISON) { + seq_printf(s, "ERROR: Corrupted log entry at offset %llu\n", iter.prev_offset); + continue; + } + + /* Get registration for this entry */ + if (entry->reg_id >= CEPH_SAN_LOG_MAX_REGISTRATIONS) { + seq_printf(s, "ERROR: Invalid registration ID %u\n", entry->reg_id); + continue; + } + + reg = &g_registrations[entry->reg_id]; + + /* Format timestamp */ + jiffies_to_formatted_time(entry->ts, time_str, sizeof(time_str)); + + /* Output the log entry with all registration info */ + seq_printf(s, "[%s] %s:%s:%u - %s\n", + time_str, + reg->file, reg->func, reg->line, + entry->buffer); + count++; + } + + if (count == 0) + seq_printf(s, "No log entries found\n"); + else + seq_printf(s, "----- End of log: %d entries -----\n", count); + + return 0; } static int print_task_cgroup(struct task_struct *task, char *cgroup_path, size_t size) diff --git a/include/linux/ceph/ceph_san_logger.h b/include/linux/ceph/ceph_san_logger.h index 5b9cf02a47742..e9870a507df26 100644 --- a/include/linux/ceph/ceph_san_logger.h +++ b/include/linux/ceph/ceph_san_logger.h @@ -7,19 +7,39 @@ #include #include #include +#include /* Maximum length of a log entry buffer */ #define CEPH_SAN_LOG_MAX_LEN 256 #define CEPH_SAN_LOG_ENTRY_POISON 0xDEADBEEF +#define CEPH_SAN_LOG_MAX_REGISTRATIONS 1024 + +/* Helper function to format jiffies into human readable time */ +int jiffies_to_formatted_time(unsigned long jiffies_value, char *buffer, size_t buffer_len); + +/* Get TLS context for current task */ +struct ceph_san_tls_ctx *ceph_san_get_tls_ctx(void); + +/* Log a message with file, function, and line information */ +void ceph_san_log(const char *file, const char *func, unsigned int line, const char *fmt, ...); + +/* Log registration structure */ +struct ceph_san_log_registration { + const char *file; /* Source file - static pointer */ + const char *func; /* Source function - static pointer */ + unsigned int line; /* Line number */ + const char *fmt; /* Format string - static pointer */ + unsigned int id; /* Unique registration ID */ + size_t params_size; /* Size of all parameters when compacted */ +}; + /* Log entry structure */ struct ceph_san_log_entry { u64 debug_poison; /* Debug poison value */ u64 ts; /* Timestamp (jiffies) */ - unsigned int line; /* Line number */ + unsigned int reg_id; /* Registration ID */ unsigned int len; /* Length of the message */ - const char *file; /* Source file */ - const char *func; /* Source function */ - char *buffer; /* Flexible array for inline buffer */ + char *buffer; /* Flexible array for inline buffer */ }; /* TLS context structure */ @@ -60,17 +80,27 @@ int ceph_san_logger_init(void); /* Clean up the logging system */ void ceph_san_logger_cleanup(void); -/* Log a message */ -void ceph_san_log(const char *file, const char *func, unsigned int line, const char *fmt, ...); +/* Register a log location and get unique ID */ +unsigned int ceph_san_log_register(const char *file, const char *func, + unsigned int line, const char *fmt); -/* Get current TLS context, creating if necessary */ -struct ceph_san_tls_ctx *ceph_san_get_tls_ctx(void); +/* Log a message using registration ID */ +void ceph_san_log_with_id(unsigned int reg_id, const char *fmt, ...); /* Helper macro for logging */ #define CEPH_SAN_LOG(fmt, ...) \ - ceph_san_log(kbasename(__FILE__), __func__, __LINE__, fmt, ##__VA_ARGS__) + do { \ + static unsigned int __log_id = 0; \ + if (unlikely(!__log_id)) \ + __log_id = ceph_san_log_register(kbasename(__FILE__), __func__, \ + __LINE__, fmt); \ + ceph_san_log_with_id(__log_id, fmt, ##__VA_ARGS__); \ + } while (0) /* Global logger instance */ extern struct ceph_san_logger g_logger; +extern struct ceph_san_log_registration g_registrations[]; + +extern void ceph_san_log_with_id_v(unsigned int reg_id, const char *fmt, va_list args); #endif /* CEPH_SAN_LOGGER_H */ \ No newline at end of file diff --git a/include/linux/ceph/ceph_san_packer.h b/include/linux/ceph/ceph_san_packer.h new file mode 100644 index 0000000000000..2a021c57217b3 --- /dev/null +++ b/include/linux/ceph/ceph_san_packer.h @@ -0,0 +1,87 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +#ifndef _CEPH_SAN_PACKER_H +#define _CEPH_SAN_PACKER_H + +#include +#include + +/* + * Elegant recursive packing macros + * + * These macros provide type-safe serialization and size calculation + * for arbitrary data with minimal recursion complexity. + */ + +/* Special terminator value for recursion */ +#define _END _END_MARKER_ + +/* SERIALIZATION MACROS - True recursion with direct pointer manipulation */ + +#define SERIALIZE(buff, ...) _SERIALIZE(buff, ##__VA_ARGS__, _END) + +#define _SERIALIZE(buff, val, ...) \ + do { \ + typeof(val) *__ptr = (buff); \ + *__ptr = (val); \ + _SERIALIZE_CHECK((typeof(val) *)__ptr + 1, ##__VA_ARGS__); \ + } while(0) + +#define _SERIALIZE_CHECK(buff, first, ...) \ + _SERIALIZE_IF_NEMPTY(first)(buff, first, ##__VA_ARGS__) + +#define _SERIALIZE_IF_NEMPTY(first) \ + _SERIALIZE_TEST(first, _END, _SERIALIZE_EMPTY, _SERIALIZE_RECURSE) + +#define _SERIALIZE_TEST(a, b, empty, recurse) \ + _SERIALIZE_TEST_(a, b) (empty, recurse) + +#define _SERIALIZE_TEST_(a, b) \ + _SERIALIZE_IS_ ## b + +#define _SERIALIZE_IS__END(empty, recurse) empty +#define _SERIALIZE_IS__END_MARKER_(empty, recurse) empty +#define _SERIALIZE_IS_default(empty, recurse) recurse + +#define _SERIALIZE_EMPTY(...) +#define _SERIALIZE_RECURSE(buff, val, ...) \ + _SERIALIZE(buff, val, ##__VA_ARGS__) + +/* SIZE CALCULATION MACROS - Elegant recursive calculation */ + +#define CALC_SIZE(...) _CALC_SIZE(##__VA_ARGS__, _END) + +#define _CALC_SIZE(val, ...) \ + _CALC_SIZE_CHECK(sizeof(typeof(val)), ##__VA_ARGS__) + +#define _CALC_SIZE_CHECK(current_size, first, ...) \ + _CALC_SIZE_IF_NEMPTY(first)(current_size, first, ##__VA_ARGS__) + +#define _CALC_SIZE_IF_NEMPTY(first) \ + _CALC_SIZE_TEST(first, _END, _CALC_SIZE_FINAL, _CALC_SIZE_CONTINUE) + +#define _CALC_SIZE_TEST(a, b, final, cont) \ + _CALC_SIZE_TEST_(a, b) (final, cont) + +#define _CALC_SIZE_TEST_(a, b) \ + _CALC_SIZE_IS_ ## b + +#define _CALC_SIZE_IS__END(final, cont) final +#define _CALC_SIZE_IS__END_MARKER_(final, cont) final +#define _CALC_SIZE_IS_default(final, cont) cont + +#define _CALC_SIZE_FINAL(size) (size) +#define _CALC_SIZE_CONTINUE(size, val, ...) \ + _CALC_SIZE_CHECK((size) + sizeof(typeof(val)), ##__VA_ARGS__) + +/* USAGE EXAMPLE: + * + * // Serialization + * int buffer[3]; + * SERIALIZE(buffer, 1, 2, 3); + * + * // Size calculation + * size_t size = CALC_SIZE(int_val, u64_val, char_val); + * + */ + +#endif /* _CEPH_SAN_PACKER_H */ \ No newline at end of file diff --git a/net/ceph/ceph_san_logger.c b/net/ceph/ceph_san_logger.c index 874b09c58ec75..30a6c01bc3d7e 100644 --- a/net/ceph/ceph_san_logger.c +++ b/net/ceph/ceph_san_logger.c @@ -5,6 +5,7 @@ #include #include #include +#include #include #define CEPH_SAN_LOG_BATCH_MAX_FULL 128 @@ -12,6 +13,13 @@ struct ceph_san_logger g_logger; EXPORT_SYMBOL(g_logger); +/* Registration table */ +struct ceph_san_log_registration g_registrations[CEPH_SAN_LOG_MAX_REGISTRATIONS]; +EXPORT_SYMBOL(g_registrations); + +static unsigned int g_next_reg_id; +static DEFINE_SPINLOCK(g_reg_lock); + static void *alloc_tls_ctx(void) { struct ceph_san_tls_ctx *ctx; @@ -110,84 +118,44 @@ struct ceph_san_tls_ctx *ceph_san_get_tls_ctx(void) EXPORT_SYMBOL(ceph_san_get_tls_ctx); /** - * ceph_san_log - Log a message - * @file: Source file name + * ceph_san_log - Log a message with file/func/line registration + * @file: Source file name (static string) + * @func: Function name (static string) * @line: Line number - * @fmt: Format string + * @fmt: Format string (static string) * - * Logs a message to the current TLS context's log buffer + * Registers the location on first call and reuses registration ID on subsequent calls. + * The static global ID is stored in the first word of the format string buffer. */ -void ceph_san_log(const char *file, const char *func, unsigned int line, const char *fmt, ...) +void ceph_san_log(const char *file, const char *func, unsigned int line, + const char *fmt, ...) { - /* Format the message into local buffer first */ - char buf[256]; - struct ceph_san_tls_ctx *ctx; - struct ceph_san_log_entry *entry; + static unsigned int *p_reg_id = NULL; + unsigned int reg_id = 0; va_list args; - u64 alloc; - int len, needed_size; - ctx = ceph_san_get_tls_ctx(); - if (!ctx) { - pr_err("Failed to get TLS context\n"); - return; + /* Check if we've registered this location before */ + if (likely(p_reg_id)) { + reg_id = *p_reg_id; + } else { + /* First call - register this location */ + reg_id = ceph_san_log_register(file, func, line, fmt); + if (!reg_id) { + /* Registration failed */ + pr_err("ceph_san_log: registration failed for %s:%s:%d\n", + file, func, line); + return; + } + + /* Store registration ID in the format string's first word */ + p_reg_id = (unsigned int *)fmt; + *p_reg_id = reg_id; } - + + /* Call the ID-based logger with the same arguments */ va_start(args, fmt); - len = vsnprintf(buf, sizeof(buf), fmt, args); + ceph_san_log_with_id_v(reg_id, fmt, args); va_end(args); - - needed_size = sizeof(*entry) + len + 1; - /* Allocate entry from pagefrag - We need a spinlock here to protect access iterators */ - spin_lock_bh(&ctx->pf.lock); - alloc = cephsan_pagefrag_alloc(&ctx->pf, needed_size); - int loop_count = 0; - while (!alloc) { - entry = cephsan_pagefrag_get_ptr_from_tail(&ctx->pf); - if (loop_count++ >= 32) { - pr_err("ceph_san_log: pagefrag stats - head: %u, tail: %u, size: %u, free: %d\n", - ctx->pf.head, ctx->pf.tail, - CEPHSAN_PAGEFRAG_SIZE, - (ctx->pf.tail > ctx->pf.head) ? - ctx->pf.tail - ctx->pf.head : - CEPHSAN_PAGEFRAG_SIZE - (ctx->pf.head - ctx->pf.tail)); - - panic("ceph_san_log: failed to allocate entry after 8 retries"); - } - if (entry->debug_poison != CEPH_SAN_LOG_ENTRY_POISON || entry->len == 0) { - pr_err("ceph_san_log: pagefrag corruption detected\n"); - pr_err(" head: %u, tail: %u, size: %u\n", - ctx->pf.head, ctx->pf.tail, CEPHSAN_PAGEFRAG_SIZE); - pr_err(" active_elements: %u, alloc_count: %u\n", - ctx->pf.active_elements, ctx->pf.alloc_count); - pr_err(" entry poison: %llx, len: %u\n", - entry->debug_poison, entry->len); - pr_err(" wrap_to_end: %u, wrap_around: %u\n", - ctx->pf.wrap_to_end, ctx->pf.wrap_around); - BUG(); - } - cephsan_pagefrag_free(&ctx->pf, entry->len); - alloc = cephsan_pagefrag_alloc(&ctx->pf, needed_size); - } - entry = cephsan_pagefrag_get_ptr(&ctx->pf, alloc); - - /* Fill in entry details */ - entry->debug_poison = CEPH_SAN_LOG_ENTRY_POISON; - entry->ts = jiffies; - entry->line = line; - entry->file = file; - entry->func = func; - if (unlikely(cephsan_pagefrag_is_wraparound(alloc))) { - entry->buffer = cephsan_pagefrag_get_ptr(&ctx->pf, 0); - } else { - entry->buffer = (char *)(entry + 1); - } - entry->len = cephsan_pagefrag_get_alloc_size(alloc); - spin_unlock_bh(&ctx->pf.lock); - - /* Copy to entry buffer */ - memcpy(entry->buffer, buf, len + 1); - entry->buffer[len] = '\0'; } EXPORT_SYMBOL(ceph_san_log); @@ -203,6 +171,7 @@ int ceph_san_logger_init(void) /* Initialize global state */ INIT_LIST_HEAD(&g_logger.contexts); spin_lock_init(&g_logger.lock); + g_next_reg_id = 1; /* Start IDs at 1, 0 is invalid */ /* Initialize allocation batch */ ret = ceph_san_batch_init(&g_logger.alloc_batch); @@ -306,4 +275,185 @@ struct ceph_san_log_entry *ceph_san_log_iter_next(struct ceph_san_log_iter *iter return entry; } -EXPORT_SYMBOL(ceph_san_log_iter_next); \ No newline at end of file +EXPORT_SYMBOL(ceph_san_log_iter_next); + +/** + * ceph_san_log_register - Register a logging location + * @file: Source file name (static string) + * @func: Function name (static string) + * @line: Line number + * @fmt: Format string (static string) + * + * Registers a logging location to avoid storing the same information in each log entry. + * Returns a registration ID or 0 on failure. + */ +unsigned int ceph_san_log_register(const char *file, const char *func, + unsigned int line, const char *fmt) +{ + unsigned int reg_id; + unsigned int params_size; + + if (!file || !func || !fmt) + return 0; + + spin_lock_bh(&g_reg_lock); + + /* Check if this is a duplicate registration */ + for (reg_id = 1; reg_id < g_next_reg_id; reg_id++) { + if (g_registrations[reg_id].file == file && + g_registrations[reg_id].func == func && + g_registrations[reg_id].line == line && + g_registrations[reg_id].fmt == fmt) { + spin_unlock_bh(&g_reg_lock); + return reg_id; + } + } + + /* Check if we have space for a new registration */ + if (g_next_reg_id >= CEPH_SAN_LOG_MAX_REGISTRATIONS) { + spin_unlock_bh(&g_reg_lock); + pr_err("Too many log registrations\n"); + return 0; + } + + /* Calculate the size of parameters when compacted */ + params_size = strlen(file) + 1 + strlen(func) + 1 + sizeof(unsigned int) + strlen(fmt) + 1; + + /* Create a new registration */ + reg_id = g_next_reg_id++; + g_registrations[reg_id].file = file; + g_registrations[reg_id].func = func; + g_registrations[reg_id].line = line; + g_registrations[reg_id].fmt = fmt; + g_registrations[reg_id].id = reg_id; + g_registrations[reg_id].params_size = params_size; + + spin_unlock_bh(&g_reg_lock); + return reg_id; +} +EXPORT_SYMBOL(ceph_san_log_register); + +/** + * ceph_san_log_with_id_v - Log a message using registration ID with va_list + * @reg_id: Registration ID + * @fmt: Format string + * @args: Variable argument list + * + * Logs a message using a pre-registered ID. The parameters are compacted + * and stored efficiently. + */ +void ceph_san_log_with_id_v(unsigned int reg_id, const char *fmt, va_list args) +{ + char buf[256]; + struct ceph_san_tls_ctx *ctx; + struct ceph_san_log_entry *entry; + struct ceph_san_log_registration *reg; + u64 alloc; + int len, needed_size; + + /* Validate registration ID */ + if (!reg_id || reg_id >= g_next_reg_id) { + pr_err("ceph_san_log: invalid registration ID %u\n", reg_id); + return; + } + + /* Get registration info */ + reg = &g_registrations[reg_id]; + if (reg->fmt != fmt) { + pr_err("ceph_san_log: format string mismatch for ID %u\n", reg_id); + return; + } + + /* Get TLS context */ + ctx = ceph_san_get_tls_ctx(); + if (!ctx) { + pr_err("Failed to get TLS context\n"); + return; + } + + /* Format message with arguments */ + len = vsnprintf(buf, sizeof(buf), fmt, args); + + /* Allocate log entry with compacted parameters */ + needed_size = sizeof(*entry) + len + 1; + + spin_lock_bh(&ctx->pf.lock); + alloc = cephsan_pagefrag_alloc(&ctx->pf, needed_size); + + /* Handle allocation failures by freeing older entries */ + int loop_count = 0; + while (!alloc) { + entry = cephsan_pagefrag_get_ptr_from_tail(&ctx->pf); + if (loop_count++ >= 32) { + pr_err("ceph_san_log: pagefrag stats - head: %u, tail: %u, size: %u, free: %d\n", + ctx->pf.head, ctx->pf.tail, + CEPHSAN_PAGEFRAG_SIZE, + (ctx->pf.tail > ctx->pf.head) ? + ctx->pf.tail - ctx->pf.head : + CEPHSAN_PAGEFRAG_SIZE - (ctx->pf.head - ctx->pf.tail)); + + panic("ceph_san_log: failed to allocate entry after 32 retries"); + } + + /* Check for corruption */ + if (entry->debug_poison != CEPH_SAN_LOG_ENTRY_POISON || entry->len == 0) { + struct ceph_san_log_registration *reg = &g_registrations[entry->reg_id]; + pr_err("ceph_san_log: pagefrag corruption detected\n"); + pr_err(" head: %u, tail: %u, size: %u\n", + ctx->pf.head, ctx->pf.tail, CEPHSAN_PAGEFRAG_SIZE); + pr_err(" active_elements: %u, alloc_count: %u\n", + ctx->pf.active_elements, ctx->pf.alloc_count); + pr_err(" entry poison: %llx, len: %u\n", + entry->debug_poison, entry->len); + pr_err(" wrap_to_end: %u, wrap_around: %u\n", + ctx->pf.wrap_to_end, ctx->pf.wrap_around); + pr_err(" location: %s:%s:%u\n", + reg->file, reg->func, reg->line); + BUG(); + } + + /* Free the entry and try again */ + cephsan_pagefrag_free(&ctx->pf, entry->len); + alloc = cephsan_pagefrag_alloc(&ctx->pf, needed_size); + } + + /* Get pointer to the allocated entry */ + entry = cephsan_pagefrag_get_ptr(&ctx->pf, alloc); + + /* Fill in entry details */ + entry->debug_poison = CEPH_SAN_LOG_ENTRY_POISON; + entry->ts = jiffies; + entry->reg_id = reg_id; + + /* Handle buffer wraparound */ + if (unlikely(cephsan_pagefrag_is_wraparound(alloc))) { + entry->buffer = cephsan_pagefrag_get_ptr(&ctx->pf, 0); + } else { + entry->buffer = (char *)(entry + 1); + } + + entry->len = len; + spin_unlock_bh(&ctx->pf.lock); + + /* Copy the formatted message to the buffer */ + memcpy(entry->buffer, buf, len + 1); + entry->buffer[len] = '\0'; +} + +/** + * ceph_san_log_with_id - Log a message using registration ID + * @reg_id: Registration ID + * @fmt: Format string + * + * Logs a message using a pre-registered ID. The parameters are compacted + * and stored efficiently. + */ +void ceph_san_log_with_id(unsigned int reg_id, const char *fmt, ...) +{ + va_list args; + + va_start(args, fmt); + ceph_san_log_with_id_v(reg_id, fmt, args); + va_end(args); +} +EXPORT_SYMBOL(ceph_san_log_with_id); \ No newline at end of file -- 2.39.5