]> git.apps.os.sepia.ceph.com Git - ceph-client.git/commitdiff
ceph_san: binary logging
authorAlex Markuze <amarkuze@redhat.com>
Tue, 25 Mar 2025 15:00:44 +0000 (15:00 +0000)
committerAlex Markuze <amarkuze@redhat.com>
Tue, 25 Mar 2025 15:00:44 +0000 (15:00 +0000)
fs/ceph/debugfs.c
include/linux/ceph/ceph_san_logger.h
include/linux/ceph/ceph_san_packer.h [new file with mode: 0644]
net/ceph/ceph_san_logger.c

index 3c0ea06c076b21e8182bee0bb5f7d1ed5ef59cd1..bc087146dd08bc2a845e02073ec6609b1224c397 100644 (file)
@@ -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)
index 5b9cf02a477427ca20c88806f63169ed2028fbff..e9870a507df266498e60c4a2f2093e39d0efd2e7 100644 (file)
@@ -7,19 +7,39 @@
 #include <linux/spinlock.h>
 #include <linux/ceph/ceph_san_batch.h>
 #include <linux/ceph/ceph_san_pagefrag.h>
+#include <linux/stdarg.h>
 
 /* 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 (file)
index 0000000..2a021c5
--- /dev/null
@@ -0,0 +1,87 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+#ifndef _CEPH_SAN_PACKER_H
+#define _CEPH_SAN_PACKER_H
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+
+/*
+ * 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
index 874b09c58ec754f3a4820f497dac8ae913103017..30a6c01bc3d7e1020c53d005f94d5d9a8e4a3e57 100644 (file)
@@ -5,6 +5,7 @@
 #include <linux/list.h>
 #include <linux/sched.h>
 #include <linux/string.h>
+#include <linux/seq_file.h>
 #include <linux/ceph/ceph_san_logger.h>
 
 #define CEPH_SAN_LOG_BATCH_MAX_FULL 128
 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