]> git-server-git.apps.pok.os.sepia.ceph.com Git - ceph-client.git/commitdiff
TLS logger
authorAlex Markuze <amarkuze@redhat.com>
Wed, 26 Feb 2025 16:49:21 +0000 (16:49 +0000)
committerAlex Markuze <amarkuze@redhat.com>
Wed, 26 Feb 2025 20:06:29 +0000 (20:06 +0000)
fs/ceph/debugfs.c
fs/ceph/super.h
include/linux/ceph/ceph_san.h
net/ceph/ceph_san.c

index 2f2bd49cc4824aa036a3cba0bfe98bbf2b57d321..f30bef4ec84609faf5c7c31a23e7eef83f552b3f 100644 (file)
@@ -25,7 +25,7 @@
 
 static int histogram_show(struct seq_file *s, void *p)
 {
-       struct ceph_san_tls_logger *tls;
+       struct ceph_san_percore_logger *pc;
        size_t cpu;
        u64 total_histogram[16] = {0};
        int i;
@@ -36,11 +36,11 @@ static int histogram_show(struct seq_file *s, void *p)
 
        /* Sum up histograms from all CPUs */
        for_each_possible_cpu(cpu) {
-               tls = &per_cpu(ceph_san_tls, cpu);
+               pc = &per_cpu(ceph_san_percore, cpu);
 
                /* Add each CPU's histogram data to the total */
                for (i = 0; i < 16; i++) {
-                               total_histogram[i] += tls->histogram.counters[i];
+                       total_histogram[i] += pc->histogram.counters[i];
                }
        }
 
@@ -54,7 +54,7 @@ static int histogram_show(struct seq_file *s, void *p)
        for (i = 0; i < 16; i++) {
                int stars = sum ? (total_histogram[i] * 128) / sum : 0;
                u64 percent = sum ? (total_histogram[i] * 100) / sum : 0;
-               seq_printf(s, "%-4d [%3.1f%%] ", i, percent);
+               seq_printf(s, "%-4d [%3llu%%] ", i, percent);
                while (stars-- > 0)
                        seq_printf(s, "*");
                seq_printf(s, "\n");
@@ -65,25 +65,25 @@ static int histogram_show(struct seq_file *s, void *p)
 
 static int ceph_san_show(struct seq_file *s, void *p)
 {
-       struct ceph_san_tls_logger *tls;
+       struct ceph_san_percore_logger *pc;
        size_t cpu;
 
        seq_printf(s, "Ceph SAN logs:\n");
        seq_printf(s, "%-16s %-8s %-32s\n",
-                  "Task", "PID", "Log");
+                          "Task", "PID", "Log");
        seq_printf(s, "--------------------------------------------------------------\n");
 
        for_each_possible_cpu(cpu) {
-               tls = &per_cpu(ceph_san_tls, cpu);
+               pc = &per_cpu(ceph_san_percore, cpu);
                int i;
 
                int idx = 0;
-               int head_idx = tls->head_idx & (CEPH_SAN_MAX_LOGS - 1);
+               int head_idx = pc->head_idx & (CEPH_SAN_MAX_LOGS - 1);
                int tail_idx = (head_idx + 1) & (CEPH_SAN_MAX_LOGS - 1);
 
                for (i = tail_idx; (i & (CEPH_SAN_MAX_LOGS -1)) != head_idx; i++) {
                        struct timespec64 ts;
-                       struct ceph_san_log_entry *log = &tls->logs[i & (CEPH_SAN_MAX_LOGS -1)];
+                       struct ceph_san_log_entry *log = &pc->logs[i & (CEPH_SAN_MAX_LOGS -1)];
                        jiffies_to_timespec64(log->ts, &ts);
 
                        if (log->ts == 0) {
@@ -443,6 +443,107 @@ static int status_show(struct seq_file *s, void *p)
        return 0;
 }
 
+static int ceph_san_contexts_show(struct seq_file *s, void *p)
+{
+       struct tls_ceph_san_context *ctx;
+       struct ceph_san_tls_logger *logger;
+       struct timespec64 ts;
+       unsigned long flags;
+       int count = 0;
+
+       seq_printf(s, "All Ceph SAN TLS contexts:\n");
+       seq_printf(s, "%-8s %-16s %-20s %-12s %-12s\n",
+                          "PID", "Task", "Last Activity", "Cache Type", "Log Count");
+       seq_printf(s, "--------------------------------------------------------------\n");
+
+       spin_lock_irqsave(&g_ceph_san_contexts_lock, flags);
+
+       list_for_each_entry(ctx, &g_ceph_san_contexts, list) {
+               logger = &ctx->logger;
+               count++;
+
+               /* Get timestamp of most recent log entry */
+               int head_idx = logger->head_idx & (CEPH_SAN_MAX_LOGS - 1);
+               struct ceph_san_log_entry_tls *entry = &logger->logs[head_idx];
+
+               if (entry->ts == 0) {
+                       seq_printf(s, "%-8s %-16s %-20s %-12s %-12d\n",
+                                         "N/A", "N/A", "N/A", "N/A", 0);
+                       continue;
+               }
+
+               jiffies_to_timespec64(entry->ts, &ts);
+
+               seq_printf(s, "%-8d %-16s %-20lld.%09ld\n",
+                               logger->pid,
+                               logger->comm,
+                                (long long)ts.tv_sec,
+                                ts.tv_nsec);
+       }
+
+       spin_unlock_irqrestore(&g_ceph_san_contexts_lock, flags);
+
+       seq_printf(s, "\nTotal contexts: %d\n", count);
+
+       return 0;
+}
+
+static int ceph_san_tls_show(struct seq_file *s, void *p)
+{
+       struct tls_ceph_san_context *ctx;
+       struct ceph_san_tls_logger *logger;
+       struct ceph_san_log_entry_tls *entry;
+       unsigned long flags;
+       int count = 0;
+
+       seq_printf(s, "All Ceph SAN TLS logs from all contexts:\n");
+       seq_printf(s, "%-8s %-16s %-20s %-8s %-s\n",
+                          "PID", "Task", "Timestamp", "Index", "Log Message");
+       seq_printf(s, "-------------------------------------------------------------------------\n");
+
+       spin_lock_irqsave(&g_ceph_san_contexts_lock, flags);
+
+       list_for_each_entry(ctx, &g_ceph_san_contexts, list) {
+               logger = &ctx->logger;
+               count++;
+
+               seq_printf(s, "\n=== Context for PID %d ===\n", logger->pid);
+
+               int idx = 0;
+               int head_idx = logger->head_idx & (CEPH_SAN_MAX_LOGS - 1);
+               int tail_idx = (head_idx + 1) & (CEPH_SAN_MAX_LOGS - 1);
+
+               for (int i = tail_idx; (i & (CEPH_SAN_MAX_LOGS - 1)) != head_idx; i++) {
+                       struct timespec64 ts;
+                       entry = &logger->logs[i & (CEPH_SAN_MAX_LOGS - 1)];
+
+                       if (entry->ts == 0 || !entry->buf) {
+                               continue;
+                       }
+
+                       jiffies_to_timespec64(entry->ts, &ts);
+
+                       seq_printf(s, "%-8d %-16s %lld.%09ld\n",
+                                         logger->pid,
+                                         logger->comm,
+                                         (long long)ts.tv_sec,
+                                         ts.tv_nsec);
+               }
+
+               seq_printf(s, "\n");
+       }
+
+       spin_unlock_irqrestore(&g_ceph_san_contexts_lock, flags);
+
+       if (count == 0) {
+               seq_printf(s, "No TLS contexts found.\n");
+       } else {
+               seq_printf(s, "\nTotal contexts: %d\n", count);
+       }
+
+       return 0;
+}
+
 DEFINE_SHOW_ATTRIBUTE(mdsmap);
 DEFINE_SHOW_ATTRIBUTE(mdsc);
 DEFINE_SHOW_ATTRIBUTE(caps);
@@ -454,6 +555,8 @@ DEFINE_SHOW_ATTRIBUTE(metrics_size);
 DEFINE_SHOW_ATTRIBUTE(metrics_caps);
 DEFINE_SHOW_ATTRIBUTE(ceph_san);
 DEFINE_SHOW_ATTRIBUTE(histogram);
+DEFINE_SHOW_ATTRIBUTE(ceph_san_contexts);
+DEFINE_SHOW_ATTRIBUTE(ceph_san_tls);
 
 
 /*
@@ -491,6 +594,8 @@ void ceph_fs_debugfs_cleanup(struct ceph_fs_client *fsc)
        debugfs_remove(fsc->debugfs_mdsc);
        debugfs_remove(fsc->debugfs_cephsan);
        debugfs_remove(fsc->debugfs_histogram);
+       debugfs_remove(fsc->debugfs_cephsan_tls);
+       debugfs_remove(fsc->debugfs_cephsan_contexts);
        debugfs_remove_recursive(fsc->debugfs_metrics_dir);
        doutc(fsc->client, "done\n");
 }
@@ -555,6 +660,19 @@ void ceph_fs_debugfs_init(struct ceph_fs_client *fsc)
                                                        fsc,
                                                        &histogram_fops);
 
+       fsc->debugfs_cephsan_tls = debugfs_create_file("cephsan_tls",
+                                                       0444,
+                                                       fsc->client->debugfs_dir,
+                                                       fsc,
+                                                       &ceph_san_tls_fops);
+
+       fsc->debugfs_cephsan_contexts = debugfs_create_file("cephsan_contexts",
+                                                       0444,
+                                                       fsc->client->debugfs_dir,
+                                                       fsc,
+                                                       &ceph_san_contexts_fops);
+
+
        fsc->debugfs_metrics_dir = debugfs_create_dir("metrics",
                                                      fsc->client->debugfs_dir);
 
index 4f901006cb48e50b3a3d39e57b9a5b828c7b2282..21c4d94c2a824286517f802b18e2ac5800a3ae9d 100644 (file)
@@ -155,6 +155,8 @@ struct ceph_fs_client {
        struct dentry *debugfs_metrics_dir;
        struct dentry *debugfs_cephsan;
        struct dentry *debugfs_histogram;
+       struct dentry *debugfs_cephsan_contexts;
+       struct dentry *debugfs_cephsan_tls;
 #endif
 
 #ifdef CONFIG_CEPH_FSCACHE
index ee1445a2f37b14412a07fe6f55909b7303de7af4..33aa9945dd1a86b773a85d6f936ee184731c0aad 100644 (file)
@@ -7,7 +7,7 @@
 #include <linux/sched.h>
 #include <linux/slab.h>
 
-DECLARE_PER_CPU(struct ceph_san_tls_logger, ceph_san_tls);
+DECLARE_PER_CPU(struct ceph_san_percore_logger, ceph_san_percore);
 DECLARE_PER_CPU(struct cephsan_pagefrag, ceph_san_pagefrag);
 
 /*
@@ -42,6 +42,7 @@ struct cephsan_pagefrag {
  */
 
 int cephsan_pagefrag_init(struct cephsan_pagefrag *pf);
+int cephsan_pagefrag_init_with_buffer(struct cephsan_pagefrag *pf, void *buffer, size_t size);
 
 
 /**
@@ -73,16 +74,27 @@ void cephsan_pagefrag_deinit(struct cephsan_pagefrag *pf);
 #ifdef CONFIG_DEBUG_FS
 #define CEPH_SAN_MAX_LOGS (8192 << 2) //4MB per core
 #define LOG_BUF_SIZE 256
+#define LOG_BUF_SMALL 128
 
 void cephsan_cleanup(void);
 int cephsan_init(void);
 
 void log_cephsan(char *buf);
+void log_cephsan_tls(char *buf);
+int cephsan_dump_all_contexts(char *buf, size_t size);
+
 #define CEPH_SAN_LOG(fmt, ...) do { \
     char buf[LOG_BUF_SIZE] = {0}; \
     snprintf(buf, LOG_BUF_SIZE, fmt, ##__VA_ARGS__); \
     log_cephsan(buf); \
 } while (0)
+
+#define CEPH_SAN_LOG_TLS(fmt, ...) do { \
+    char buf[LOG_BUF_SIZE] = {0}; \
+    snprintf(buf, LOG_BUF_SIZE, fmt, ##__VA_ARGS__); \
+    log_cephsan_tls(buf); \
+} while (0)
+
 /*
  * Internal definitions for Ceph SAN logs.
  * These definitions are not part of the public API but are required by debugfs.c.
@@ -94,18 +106,43 @@ struct ceph_san_log_entry {
     pid_t pid;
     u32 len;
 };
+
+struct ceph_san_log_entry_tls {
+    u64 ts;
+    char *buf;
+};
 struct histogram {
        u64 counters[16];
 };
-struct ceph_san_tls_logger {
+
+struct ceph_san_percore_logger {
     size_t head_idx;
     struct page *pages;
     struct ceph_san_log_entry *logs;
     struct histogram histogram;
 };
+struct ceph_san_tls_logger {
+    char comm[TASK_COMM_LEN];
+    pid_t pid;
+    size_t head_idx;
+    struct ceph_san_log_entry_tls logs[CEPH_SAN_MAX_LOGS];
+};
+
+/* Bundled TLS context containing both logger and memory caches */
+struct tls_ceph_san_context {
+    struct ceph_san_tls_logger logger;
+    struct list_head list;  /* For global list of contexts */
+    /* We no longer use pagefrag for log entries */
+};
+
+/* Global list of all TLS contexts and its protection lock */
+extern struct list_head g_ceph_san_contexts;
+extern spinlock_t g_ceph_san_contexts_lock;
+
 #else /* CONFIG_DEBUG_FS */
 
 #define CEPH_SAN_LOG(param) do {} while (0)
+#define CEPH_SAN_LOG_TLS(param) do {} while (0)
 
 static inline void cephsan_cleanup(void) {}
 static inline int __init cephsan_init(void) { return 0; }
index 5b41ce8b0f12b99dd49de9f65c635777c4ee27c3..a54fc0ba300b59e7a7487fefba5717b6f14adf03 100644 (file)
 #include <linux/mm.h>
 
 /* Use per-core TLS logger; no global list or lock needed */
-DEFINE_PER_CPU(struct ceph_san_tls_logger, ceph_san_tls);
-EXPORT_SYMBOL(ceph_san_tls);
+DEFINE_PER_CPU(struct ceph_san_percore_logger, ceph_san_percore);
+EXPORT_SYMBOL(ceph_san_percore);
 
 DEFINE_PER_CPU(struct cephsan_pagefrag, ceph_san_pagefrag);
-EXPORT_SYMBOL(ceph_san_pagefrag);
 
+/* Global list of all TLS contexts and its protection lock */
+LIST_HEAD(g_ceph_san_contexts);
+DEFINE_SPINLOCK(g_ceph_san_contexts_lock);
+EXPORT_SYMBOL(g_ceph_san_contexts);
+EXPORT_SYMBOL(g_ceph_san_contexts_lock);
+
+/* Memory caches for log entries */
+struct kmem_cache *ceph_san_log_128_cache;
+struct kmem_cache *ceph_san_log_256_cache;
+
+struct kmem_cache *ceph_san_tls_logger_cache;
 
 static inline void *cephsan_pagefrag_get_ptr(struct cephsan_pagefrag *pf, u64 val);
 /* The definitions for struct ceph_san_log_entry and struct ceph_san_tls_logger
  * have been moved to cephsan.h (under CONFIG_DEBUG_FS) to avoid duplication.
  */
 
-void log_cephsan(char *buf) {
+
+/* Release function for TLS storage */
+static void ceph_san_tls_release(void *ptr)
+{
+    struct tls_ceph_san_context *context = ptr;
+    if (!context)
+        return;
+
+    /* Remove from global list with lock protection */
+    spin_lock(&g_ceph_san_contexts_lock);
+    list_del(&context->list);
+    spin_unlock(&g_ceph_san_contexts_lock);
+
+    /* Free all log entries */
+        int head_idx = context->logger.head_idx & (CEPH_SAN_MAX_LOGS - 1);
+        int tail_idx = (head_idx + 1) & (CEPH_SAN_MAX_LOGS - 1);
+
+    for (int i = tail_idx; (i & (CEPH_SAN_MAX_LOGS - 1)) != head_idx; i++) {
+        struct ceph_san_log_entry_tls *entry = &context->logger.logs[i & (CEPH_SAN_MAX_LOGS - 1)];
+        if (entry->buf) {
+            if (entry->ts & 0x1)
+                    kmem_cache_free(ceph_san_log_256_cache, entry->buf);
+                       else
+                    kmem_cache_free(ceph_san_log_128_cache, entry->buf);
+                       entry->buf = NULL;
+               }
+       }
+
+    kmem_cache_free(ceph_san_tls_logger_cache, context);
+}
+
+static struct tls_ceph_san_context *get_cephsan_context(void) {
+    struct tls_ceph_san_context *context;
+
+    context = current->tls.state;
+    if (context)
+        return context;
+
+    context = kmem_cache_alloc(ceph_san_tls_logger_cache, GFP_KERNEL);
+       if (!context)
+               return NULL;
+
+       context->logger.pid = current->pid;
+       memcpy(context->logger.comm, current->comm, TASK_COMM_LEN);
+
+     /* Initialize list entry */
+    INIT_LIST_HEAD(&context->list);
+
+    /* Add to global list with lock protection */
+    spin_lock(&g_ceph_san_contexts_lock);
+    list_add(&context->list, &g_ceph_san_contexts);
+    spin_unlock(&g_ceph_san_contexts_lock);
+
+    current->tls.state = context;
+    current->tls.release = ceph_san_tls_release;
+    return context;
+}
+
+void log_cephsan_tls(char *buf) {
+    /* Use the task's TLS storage */
+    int len = strlen(buf);
+    struct tls_ceph_san_context *ctx;
+    struct ceph_san_tls_logger *logger;
+    char *new_buf;
+
+    ctx = get_cephsan_context();
+    if (!ctx)
+        return;
+
+    logger = &ctx->logger;
+
+    /* Log the message */
+    int head_idx = logger->head_idx + 1 & (CEPH_SAN_MAX_LOGS - 1);
+    struct ceph_san_log_entry_tls *entry = &logger->logs[head_idx];
+
+    /* Only free and reallocate if sizes differ */
+    if (!entry->buf || (entry->ts & 0x1) != (len > LOG_BUF_SMALL)) {
+        if (entry->buf) {
+            if (entry->ts & 0x1)
+                kmem_cache_free(ceph_san_log_256_cache, entry->buf);
+            else
+                kmem_cache_free(ceph_san_log_128_cache, entry->buf);
+            entry->buf = NULL;
+        }
+
+        /* Allocate new buffer from appropriate cache */
+        if (len <= LOG_BUF_SMALL) {
+            new_buf = kmem_cache_alloc(ceph_san_log_128_cache, GFP_KERNEL);
+                       entry->ts = jiffies | 0x0;
+        } else {
+            new_buf = kmem_cache_alloc(ceph_san_log_256_cache, GFP_KERNEL);
+                       entry->ts = jiffies | 0x1;
+        }
+    } else {
+        /* Reuse existing buffer since size category hasn't changed */
+        new_buf = entry->buf;
+    }
+
+    if (!new_buf)
+        return;
+
+    buf[len-1] = '\0';
+    entry->buf = new_buf;
+    memcpy(entry->buf, buf, len);
+
+    logger->head_idx = head_idx;
+}
+
+static void log_cephsan_percore(char *buf) {
     /* Use the per-core TLS logger */
     u64 buf_idx;
     int len = strlen(buf);
-    struct ceph_san_tls_logger *tls = this_cpu_ptr(&ceph_san_tls);
+    struct ceph_san_percore_logger *pc = this_cpu_ptr(&ceph_san_percore);
     struct cephsan_pagefrag *pf = this_cpu_ptr(&ceph_san_pagefrag);
 
-    int head_idx = tls->head_idx + 1 & (CEPH_SAN_MAX_LOGS - 1);
-    int pre_len = tls->logs[head_idx].len;
+    int head_idx = pc->head_idx + 1 & (CEPH_SAN_MAX_LOGS - 1);
+    int pre_len = pc->logs[head_idx].len;
 
     buf[len-1] = '\0';
-    tls->logs[head_idx].pid = current->pid;
-    tls->logs[head_idx].ts = jiffies;
-    memcpy(tls->logs[head_idx].comm, current->comm, TASK_COMM_LEN);
+    pc->logs[head_idx].pid = current->pid;
+    pc->logs[head_idx].ts = jiffies;
+    memcpy(pc->logs[head_idx].comm, current->comm, TASK_COMM_LEN);
 
     cephsan_pagefrag_free(pf, pre_len);
-    tls->logs[head_idx].len = 0;
+    pc->logs[head_idx].len = 0;
 
     buf_idx = cephsan_pagefrag_alloc(pf, len);
     if (buf_idx) {
-               tls->head_idx = head_idx;
-               tls->histogram.counters[len >> 3]++;
-               tls->logs[head_idx].len = len;
-        tls->logs[head_idx].buf = cephsan_pagefrag_get_ptr(pf, buf_idx);
-               memcpy(tls->logs[head_idx].buf, buf, len);
+        pc->head_idx = head_idx;
+        pc->histogram.counters[len >> 3]++;
+        pc->logs[head_idx].len = len;
+        pc->logs[head_idx].buf = cephsan_pagefrag_get_ptr(pf, buf_idx);
+        memcpy(pc->logs[head_idx].buf, buf, len);
     }
 }
+
+void log_cephsan(char *buf) {
+    log_cephsan_percore(buf);
+    log_cephsan_tls(buf);
+}
 EXPORT_SYMBOL(log_cephsan);
 
 /* Cleanup function to free all TLS logger objects.
@@ -53,42 +176,99 @@ EXPORT_SYMBOL(log_cephsan);
  */
 void cephsan_cleanup(void)
 {
-       int cpu;
-       struct ceph_san_tls_logger *tls;
-
-       for_each_possible_cpu(cpu) {
-               tls = per_cpu_ptr(&ceph_san_tls, cpu);
-               if (tls->pages) {
-                       free_pages((unsigned long)tls->pages, get_order(CEPH_SAN_MAX_LOGS * sizeof(struct ceph_san_log_entry)));
-                       tls->pages = NULL;
-               }
-       }
+    int cpu;
+    struct ceph_san_percore_logger *pc;
+
+    for_each_possible_cpu(cpu) {
+        pc = per_cpu_ptr(&ceph_san_percore, cpu);
+        if (pc->pages) {
+            free_pages((unsigned long)pc->pages, get_order(CEPH_SAN_MAX_LOGS * sizeof(struct ceph_san_log_entry)));
+            pc->pages = NULL;
+        }
+    }
+
+       /* Let the TLS contexts cleanup lazily */
+    if (ceph_san_tls_logger_cache) {
+        kmem_cache_destroy(ceph_san_tls_logger_cache);
+        ceph_san_tls_logger_cache = NULL;
+    }
+
+    if (ceph_san_log_128_cache) {
+        kmem_cache_destroy(ceph_san_log_128_cache);
+        ceph_san_log_128_cache = NULL;
+    }
+
+    if (ceph_san_log_256_cache) {
+        kmem_cache_destroy(ceph_san_log_256_cache);
+        ceph_san_log_256_cache = NULL;
+    }
 }
 EXPORT_SYMBOL(cephsan_cleanup);
+
 /* Initialize the Ceph SAN logging infrastructure.
  * Call this at module init to set up the global list and lock.
  */
 int cephsan_init(void)
 {
-       int cpu;
-       struct ceph_san_tls_logger *tls;
-       struct cephsan_pagefrag *pf;
-
-       for_each_possible_cpu(cpu) {
-               tls = per_cpu_ptr(&ceph_san_tls, cpu);
-               tls->pages = alloc_pages(GFP_KERNEL, get_order(CEPH_SAN_MAX_LOGS * sizeof(struct ceph_san_log_entry)));
-               if (!tls->pages) {
-                       pr_err("Failed to allocate TLS logs for CPU %d\n", cpu);
-                       return -ENOMEM;
-               }
-               tls->logs = (struct ceph_san_log_entry *)page_address(tls->pages);
-       }
+    int cpu;
+    struct ceph_san_percore_logger *pc;
+    struct cephsan_pagefrag *pf;
 
-       for_each_possible_cpu(cpu) {
-               pf = per_cpu_ptr(&ceph_san_pagefrag, cpu);
-               cephsan_pagefrag_init(pf);
-       }
-       return 0;
+    /* Initialize the global list */
+    INIT_LIST_HEAD(&g_ceph_san_contexts);
+
+    /* Create memory caches for log entries */
+    ceph_san_log_128_cache = kmem_cache_create("ceph_san_log_128",
+                                             LOG_BUF_SMALL,
+                                             0, SLAB_HWCACHE_ALIGN,
+                                             NULL);
+    if (!ceph_san_log_128_cache)
+        goto cleanup_128_cache;
+
+    ceph_san_log_256_cache = kmem_cache_create("ceph_san_log_256",
+                                             LOG_BUF_SIZE,
+                                             0, SLAB_HWCACHE_ALIGN,
+                                             NULL);
+    if (!ceph_san_log_256_cache)
+        goto cleanup_256_cache;
+
+    ceph_san_tls_logger_cache = kmem_cache_create("ceph_san_tls_logger",
+                                             sizeof(struct tls_ceph_san_context),
+                                             0, SLAB_HWCACHE_ALIGN,
+                                             NULL);
+    if (!ceph_san_tls_logger_cache)
+        goto cleanup_logger_cache;
+
+    for_each_possible_cpu(cpu) {
+        pc = per_cpu_ptr(&ceph_san_percore, cpu);
+        pc->pages = alloc_pages(GFP_KERNEL, get_order(CEPH_SAN_MAX_LOGS * sizeof(struct ceph_san_log_entry)));
+        if (!pc->pages) {
+            pr_err("Failed to allocate TLS logs for CPU %d\n", cpu);
+            goto cleanup;
+        }
+        pc->logs = (struct ceph_san_log_entry *)page_address(pc->pages);
+    }
+
+    for_each_possible_cpu(cpu) {
+        pf = per_cpu_ptr(&ceph_san_pagefrag, cpu);
+        cephsan_pagefrag_init(pf);
+    }
+    return 0;
+
+cleanup:
+    cephsan_cleanup();
+    return -ENOMEM;
+
+cleanup_logger_cache:
+    kmem_cache_destroy(ceph_san_log_256_cache);
+    ceph_san_log_256_cache = NULL;
+
+cleanup_256_cache:
+    kmem_cache_destroy(ceph_san_log_128_cache);
+    ceph_san_log_128_cache = NULL;
+
+cleanup_128_cache:
+    return -ENOMEM;
 }
 EXPORT_SYMBOL(cephsan_init);
 
@@ -112,6 +292,24 @@ int cephsan_pagefrag_init(struct cephsan_pagefrag *pf)
 }
 EXPORT_SYMBOL(cephsan_pagefrag_init);
 
+/**
+ * cephsan_pagefrag_init_with_buffer - Initialize pagefrag with an existing buffer
+ * @pf: pagefrag allocator to initialize
+ * @buffer: pre-allocated buffer to use
+ * @size: size of the buffer
+ *
+ * Return: 0 on success
+ */
+int cephsan_pagefrag_init_with_buffer(struct cephsan_pagefrag *pf, void *buffer, size_t size)
+{
+    pf->pages = NULL; /* No pages allocated, using provided buffer */
+    pf->buffer = buffer;
+    pf->head = 0;
+    pf->tail = 0;
+    return 0;
+}
+EXPORT_SYMBOL(cephsan_pagefrag_init_with_buffer);
+
 /**
  * cephsan_pagefrag_alloc - Allocate bytes from the pagefrag buffer.
  * @n: number of bytes to allocate.
@@ -187,8 +385,85 @@ EXPORT_SYMBOL(cephsan_pagefrag_free);
  */
 void cephsan_pagefrag_deinit(struct cephsan_pagefrag *pf)
 {
-       kfree(pf->buffer);
+    if (pf->pages) {
+        free_pages((unsigned long)pf->pages, get_order(CEPHSAN_PAGEFRAG_SIZE));
+        pf->pages = NULL;
+    }
+    /* Don't free buffer if it was provided externally */
        pf->buffer = NULL;
        pf->head = pf->tail = 0;
 }
 EXPORT_SYMBOL(cephsan_pagefrag_deinit);
+
+/**
+ * cephsan_dump_all_contexts - Dump logs from all TLS contexts to a buffer
+ * @buf: Buffer to write logs to
+ * @size: Size of the buffer
+ *
+ * This function iterates through all TLS contexts in the global list and
+ * dumps their logs to the provided buffer. It's useful for debugging and
+ * crash analysis.
+ *
+ * Return: Number of bytes written to the buffer
+ */
+int cephsan_dump_all_contexts(char *buf, size_t size)
+{
+    struct tls_ceph_san_context *ctx;
+    struct ceph_san_tls_logger *tls;
+    struct ceph_san_log_entry_tls *entry;
+    unsigned long flags;
+    int len = 0;
+    int count = 0;
+
+    if (!buf || size == 0)
+        return 0;
+
+    len += snprintf(buf + len, size - len,
+                   "=== Ceph SAN TLS logs from all contexts ===\n");
+
+    spin_lock_irqsave(&g_ceph_san_contexts_lock, flags);
+
+    list_for_each_entry(ctx, &g_ceph_san_contexts, list) {
+        tls = &ctx->logger;
+        count++;
+
+        if (len >= size - 1)
+            break;
+
+        len += snprintf(buf + len, size - len,
+                       "\n=== Context %d (PID %d, comm %s) ===\n",
+                       count, tls->pid, tls->comm);
+        int head_idx = tls->head_idx & (CEPH_SAN_MAX_LOGS - 1);
+        int tail_idx = (head_idx + 1) & (CEPH_SAN_MAX_LOGS - 1);
+
+        for (int i = tail_idx; (i & (CEPH_SAN_MAX_LOGS - 1)) != head_idx; i++) {
+            struct timespec64 ts;
+            entry = &tls->logs[i & (CEPH_SAN_MAX_LOGS - 1)];
+
+            if (entry->ts == 0 || !entry->buf)
+                continue;
+
+            if (len >= size - 1)
+                break;
+
+            jiffies_to_timespec64(entry->ts, &ts);
+
+            len += snprintf(buf + len, size - len,
+                           "[%lld.%09ld] : %s\n",
+                           (long long)ts.tv_sec,
+                           ts.tv_nsec,
+                           entry->buf ? entry->buf : "(null)");
+        }
+    }
+
+    spin_unlock_irqrestore(&g_ceph_san_contexts_lock, flags);
+
+    if (count == 0 && len < size - 1) {
+        len += snprintf(buf + len, size - len, "No TLS contexts found.\n");
+    } else if (len < size - 1) {
+        len += snprintf(buf + len, size - len, "\nTotal contexts: %d\n", count);
+    }
+
+    return len;
+}
+EXPORT_SYMBOL(cephsan_dump_all_contexts);