From 284eb81c9d23609f54d9e015ce2c40076e8d8a87 Mon Sep 17 00:00:00 2001 From: Alex Markuze Date: Wed, 26 Feb 2025 16:49:21 +0000 Subject: [PATCH] TLS logger --- fs/ceph/debugfs.c | 136 ++++++++++++- fs/ceph/super.h | 2 + include/linux/ceph/ceph_san.h | 41 +++- net/ceph/ceph_san.c | 365 +++++++++++++++++++++++++++++----- 4 files changed, 488 insertions(+), 56 deletions(-) diff --git a/fs/ceph/debugfs.c b/fs/ceph/debugfs.c index 2f2bd49cc4824..f30bef4ec8460 100644 --- a/fs/ceph/debugfs.c +++ b/fs/ceph/debugfs.c @@ -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); diff --git a/fs/ceph/super.h b/fs/ceph/super.h index 4f901006cb48e..21c4d94c2a824 100644 --- a/fs/ceph/super.h +++ b/fs/ceph/super.h @@ -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 diff --git a/include/linux/ceph/ceph_san.h b/include/linux/ceph/ceph_san.h index ee1445a2f37b1..33aa9945dd1a8 100644 --- a/include/linux/ceph/ceph_san.h +++ b/include/linux/ceph/ceph_san.h @@ -7,7 +7,7 @@ #include #include -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; } diff --git a/net/ceph/ceph_san.c b/net/ceph/ceph_san.c index 5b41ce8b0f12b..a54fc0ba300b5 100644 --- a/net/ceph/ceph_san.c +++ b/net/ceph/ceph_san.c @@ -7,45 +7,168 @@ #include /* 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); -- 2.39.5