]> git.apps.os.sepia.ceph.com Git - ceph-client.git/commitdiff
proper isolation tls-tracing-only
authorAlex Markuze <amarkuze@redhat.com>
Wed, 13 Aug 2025 16:27:44 +0000 (16:27 +0000)
committerAlex Markuze <amarkuze@redhat.com>
Wed, 13 Aug 2025 16:27:44 +0000 (16:27 +0000)
fs/ceph/Makefile
fs/ceph/blog_client.c [new file with mode: 0644]
fs/ceph/blog_debugfs.c [new file with mode: 0644]
fs/ceph/blog_integration_example.c [new file with mode: 0644]
include/linux/blog/blog.h
include/linux/blog/blog_module.h [new file with mode: 0644]
include/linux/blog/blog_ser.h
include/linux/ceph/ceph_blog.h [new file with mode: 0644]
include/linux/ceph/ceph_san_ser.h
lib/blog/Makefile
lib/blog/blog_module.c [new file with mode: 0644]

index 1f77ca04c426fdb9be333b891ebbdee504e64d30..ccb542870ab3f696bba49c105a96c5034879dcd9 100644 (file)
@@ -10,6 +10,8 @@ ceph-y := super.o inode.o dir.o file.o locks.o addr.o ioctl.o \
        mds_client.o mdsmap.o strings.o ceph_frag.o \
        debugfs.o util.o metric.o
 
+ceph-$(CONFIG_BLOG) += blog_client.o blog_debugfs.o
+
 ceph-$(CONFIG_CEPH_FSCACHE) += cache.o
 ceph-$(CONFIG_CEPH_FS_POSIX_ACL) += acl.o
 ceph-$(CONFIG_FS_ENCRYPTION) += crypto.o
diff --git a/fs/ceph/blog_client.c b/fs/ceph/blog_client.c
new file mode 100644 (file)
index 0000000..f11e627
--- /dev/null
@@ -0,0 +1,217 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Ceph client ID management for BLOG integration
+ *
+ * Maintains mapping between Ceph's fsid/global_id and BLOG client IDs
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/ceph/ceph_debug.h>
+#include <linux/ceph/libceph.h>
+#include <linux/ceph/ceph_blog.h>
+#include <linux/blog/blog.h>
+
+/* Ceph's BLOG module context */
+struct blog_module_context *ceph_blog_ctx = NULL;
+EXPORT_SYMBOL(ceph_blog_ctx);
+
+/* Global client mapping state */
+static struct {
+       struct ceph_blog_client_info client_map[CEPH_BLOG_MAX_CLIENTS];
+       u32 next_client_id;
+       spinlock_t lock;
+       bool initialized;
+} ceph_blog_state = {
+       .next_client_id = 1,  /* Start from 1, 0 is reserved */
+       .lock = __SPIN_LOCK_UNLOCKED(ceph_blog_state.lock),
+       .initialized = false,
+};
+
+/**
+ * ceph_blog_init - Initialize Ceph BLOG integration
+ *
+ * Creates a module-specific BLOG context for Ceph and initializes
+ * the client ID mapping state.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int ceph_blog_init(void)
+{
+       if (ceph_blog_state.initialized)
+               return 0;
+
+       /* Create Ceph's module-specific BLOG context */
+       ceph_blog_ctx = blog_module_init("ceph");
+       if (!ceph_blog_ctx) {
+               pr_err("ceph: Failed to initialize BLOG module context\n");
+               return -ENOMEM;
+       }
+
+       /* Initialize client mapping state */
+       memset(ceph_blog_state.client_map, 0, sizeof(ceph_blog_state.client_map));
+       ceph_blog_state.next_client_id = 1;
+       ceph_blog_state.initialized = true;
+       
+       pr_info("ceph: BLOG module context and client mapping initialized\n");
+       return 0;
+}
+EXPORT_SYMBOL(ceph_blog_init);
+
+/**
+ * ceph_blog_cleanup - Clean up Ceph BLOG integration
+ *
+ * Cleans up Ceph's module-specific BLOG context and client mappings.
+ */
+void ceph_blog_cleanup(void)
+{
+       if (!ceph_blog_state.initialized)
+               return;
+
+       /* Clean up client mapping state */
+       spin_lock(&ceph_blog_state.lock);
+       memset(ceph_blog_state.client_map, 0, sizeof(ceph_blog_state.client_map));
+       ceph_blog_state.next_client_id = 1;
+       ceph_blog_state.initialized = false;
+       spin_unlock(&ceph_blog_state.lock);
+       
+       /* Clean up module-specific BLOG context */
+       if (ceph_blog_ctx) {
+               blog_module_cleanup(ceph_blog_ctx);
+               ceph_blog_ctx = NULL;
+       }
+       
+       pr_info("ceph: BLOG module context and client mapping cleaned up\n");
+}
+EXPORT_SYMBOL(ceph_blog_cleanup);
+
+/**
+ * ceph_blog_check_client_id - Check if a client ID matches the given fsid:global_id pair
+ * @id: Client ID to check
+ * @fsid: Client FSID to compare
+ * @global_id: Client global ID to compare
+ *
+ * This preserves the exact functionality of ceph_san_check_client_id.
+ * Returns the actual ID of the pair. If the given ID doesn't match, scans for
+ * existing matches or allocates a new ID if no match is found.
+ *
+ * Return: Client ID for this fsid/global_id pair
+ */
+u32 ceph_blog_check_client_id(u32 id, const char *fsid, u64 global_id)
+{
+       u32 found_id = 0;
+       struct ceph_blog_client_info *entry;
+       u32 max_id;
+
+       if (!ceph_blog_state.initialized)
+               ceph_blog_init();
+
+       /* First check if the given ID matches */
+       if (id != 0 && id < CEPH_BLOG_MAX_CLIENTS) {
+               entry = &ceph_blog_state.client_map[id];
+               if (memcmp(entry->fsid, fsid, sizeof(entry->fsid)) == 0 &&
+                   entry->global_id == global_id) {
+                       found_id = id;
+                       goto out_fast;
+               }
+       }
+
+       spin_lock(&ceph_blog_state.lock);
+       max_id = ceph_blog_state.next_client_id;
+
+       /* Scan for existing match */
+       for (id = 1; id < max_id && id < CEPH_BLOG_MAX_CLIENTS; id++) {
+               entry = &ceph_blog_state.client_map[id];
+               if (memcmp(entry->fsid, fsid, sizeof(entry->fsid)) == 0 &&
+                   entry->global_id == global_id) {
+                       found_id = id;
+                       goto out;
+               }
+       }
+
+       /* No match found, allocate new ID */
+       found_id = ceph_blog_state.next_client_id++;
+       if (found_id >= CEPH_BLOG_MAX_CLIENTS) {
+               /* If we run out of IDs, reuse ID 1 */
+               pr_warn("ceph: BLOG client ID overflow, reusing ID 1\n");
+               found_id = 1;
+               ceph_blog_state.next_client_id = 2;
+       }
+       /* Use %pU to print fsid like the rest of Ceph does */
+       pr_info("ceph: allocating new BLOG client ID %u for fsid=%pU global_id=%llu\n",
+               found_id, fsid, global_id);
+       
+       entry = &ceph_blog_state.client_map[found_id];
+       memcpy(entry->fsid, fsid, sizeof(entry->fsid));
+       entry->global_id = global_id;
+
+out:
+       spin_unlock(&ceph_blog_state.lock);
+out_fast:
+       return found_id;
+}
+EXPORT_SYMBOL(ceph_blog_check_client_id);
+
+/**
+ * ceph_blog_get_client_info - Get client info for a given ID
+ * @id: Client ID
+ *
+ * Return: Client information for this ID, or NULL if invalid
+ */
+const struct ceph_blog_client_info *ceph_blog_get_client_info(u32 id)
+{
+       if (!ceph_blog_state.initialized || id == 0 || id >= CEPH_BLOG_MAX_CLIENTS)
+               return NULL;
+       return &ceph_blog_state.client_map[id];
+}
+EXPORT_SYMBOL(ceph_blog_get_client_info);
+
+/**
+ * ceph_blog_client_des_callback - Deserialization callback for Ceph client info
+ * @buf: Output buffer
+ * @size: Buffer size
+ * @client_id: Client ID to deserialize
+ *
+ * This is the callback that BLOG will use to deserialize client information.
+ *
+ * Return: Number of bytes written to buffer
+ */
+int ceph_blog_client_des_callback(char *buf, size_t size, u8 client_id)
+{
+       const struct ceph_blog_client_info *info;
+
+       if (!buf || !size)
+               return -EINVAL;
+
+       info = ceph_blog_get_client_info(client_id);
+       if (!info) {
+               return snprintf(buf, size, "[unknown_client_%u]", client_id);
+       }
+
+       /* Use %pU to format fsid, matching doutc and other Ceph client logging */
+       return snprintf(buf, size, "[%pU %llu] ",
+                       info->fsid, info->global_id);
+}
+EXPORT_SYMBOL(ceph_blog_client_des_callback);
+
+/**
+ * ceph_blog_get_client_id - Get or allocate client ID for a Ceph client
+ * @client: Ceph client structure
+ *
+ * Return: Client ID for this client
+ */
+u32 ceph_blog_get_client_id(struct ceph_client *client)
+{
+       static u32 cached_id = 0;
+       
+       if (!client)
+               return 0;
+       
+       /* Use ceph_blog_check_client_id which handles caching internally */
+       cached_id = ceph_blog_check_client_id(cached_id, 
+                                             client->fsid.fsid,
+                                             client->monc.auth->global_id);
+       return cached_id;
+}
+EXPORT_SYMBOL(ceph_blog_get_client_id);
diff --git a/fs/ceph/blog_debugfs.c b/fs/ceph/blog_debugfs.c
new file mode 100644 (file)
index 0000000..147ca2b
--- /dev/null
@@ -0,0 +1,303 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Ceph BLOG debugfs interface
+ *
+ * Provides debugfs entries to view and manage BLOG entries for Ceph
+ */
+
+#include <linux/module.h>
+#include <linux/debugfs.h>
+#include <linux/seq_file.h>
+#include <linux/slab.h>
+#include <linux/ceph/ceph_debug.h>
+#include <linux/ceph/ceph_blog.h>
+#include <linux/blog/blog.h>
+#include <linux/blog/blog_des.h>
+
+static struct dentry *ceph_blog_debugfs_dir;
+
+/**
+ * blog_entries_show - Show all BLOG entries for Ceph
+ * 
+ * Iterates through all contexts and their pagefrags, deserializing entries
+ * using BLOG's deserialization with Ceph's client callback
+ */
+static int blog_entries_show(struct seq_file *s, void *p)
+{
+       struct blog_tls_ctx *ctx;
+       struct blog_log_iter iter;
+       struct blog_log_entry *entry;
+       char output_buf[1024];
+       int ret;
+       int entry_count = 0;
+       int ctx_count = 0;
+
+       seq_printf(s, "Ceph BLOG Entries\n");
+       seq_printf(s, "=================\n\n");
+
+       /* Access the global logger - need to be careful here */
+       spin_lock(&g_blog_logger.lock);
+       
+       list_for_each_entry(ctx, &g_blog_logger.contexts, list) {
+               ctx_count++;
+               seq_printf(s, "Context %d (ID: %llu, PID: %d, Comm: %s)\n",
+                         ctx_count, ctx->id, ctx->pid, ctx->comm);
+               seq_printf(s, "  Base jiffies: %lu, Refcount: %d\n",
+                         ctx->base_jiffies, atomic_read(&ctx->refcount));
+               
+               /* Initialize iterator for this context's pagefrag */
+               blog_log_iter_init(&iter, &ctx->pf);
+               
+               /* Iterate through all entries in this context */
+               while ((entry = blog_log_iter_next(&iter)) != NULL) {
+                       entry_count++;
+                       
+                       /* Clear output buffer */
+                       memset(output_buf, 0, sizeof(output_buf));
+                       
+                       /* Use blog_des_entry with Ceph's client callback */
+                       ret = blog_des_entry(entry, output_buf, sizeof(output_buf),
+                                           ceph_blog_client_des_callback);
+                       
+                       if (ret < 0) {
+                               seq_printf(s, "  Entry %d: [Error deserializing: %d]\n",
+                                         entry_count, ret);
+                       } else {
+                               /* Show entry details */
+                               seq_printf(s, "  Entry %d (ts_delta=%u, src=%u, client=%u, len=%u):\n",
+                                         entry_count, entry->ts_delta, entry->source_id, 
+                                         entry->client_id, entry->len);
+                               seq_printf(s, "    %s\n", output_buf);
+                       }
+               }
+               seq_printf(s, "\n");
+       }
+       
+       spin_unlock(&g_blog_logger.lock);
+       
+       seq_printf(s, "Total contexts: %d\n", ctx_count);
+       seq_printf(s, "Total entries: %d\n", entry_count);
+       
+       return 0;
+}
+
+static int blog_entries_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, blog_entries_show, inode->i_private);
+}
+
+static const struct file_operations blog_entries_fops = {
+       .open = blog_entries_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+/**
+ * blog_stats_show - Show BLOG statistics
+ */
+static int blog_stats_show(struct seq_file *s, void *p)
+{
+       seq_printf(s, "Ceph BLOG Statistics\n");
+       seq_printf(s, "====================\n\n");
+       
+       seq_printf(s, "Global Logger State:\n");
+       seq_printf(s, "  Total contexts allocated: %lu\n", 
+                 g_blog_logger.total_contexts_allocated);
+       seq_printf(s, "  Next context ID: %llu\n", g_blog_logger.next_ctx_id);
+       seq_printf(s, "  Next source ID: %u\n", 
+                 atomic_read(&g_blog_logger.next_source_id));
+       
+       seq_printf(s, "\nAllocation Batch:\n");
+       seq_printf(s, "  Full magazines: %u\n", g_blog_logger.alloc_batch.nr_full);
+       seq_printf(s, "  Empty magazines: %u\n", g_blog_logger.alloc_batch.nr_empty);
+       
+       seq_printf(s, "\nLog Batch:\n");
+       seq_printf(s, "  Full magazines: %u\n", g_blog_logger.log_batch.nr_full);
+       seq_printf(s, "  Empty magazines: %u\n", g_blog_logger.log_batch.nr_empty);
+       
+       return 0;
+}
+
+static int blog_stats_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, blog_stats_show, inode->i_private);
+}
+
+static const struct file_operations blog_stats_fops = {
+       .open = blog_stats_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+/**
+ * blog_sources_show - Show all registered source locations
+ */
+static int blog_sources_show(struct seq_file *s, void *p)
+{
+       struct blog_source_info *source;
+       u32 id;
+       int count = 0;
+       
+       seq_printf(s, "Ceph BLOG Source Locations\n");
+       seq_printf(s, "===========================\n\n");
+       
+       for (id = 1; id < BLOG_MAX_SOURCE_IDS; id++) {
+               source = blog_get_source_info(id);
+               if (!source || !source->file)
+                       continue;
+               
+               count++;
+               seq_printf(s, "ID %u: %s:%s:%u\n", id, 
+                         source->file, source->func, source->line);
+               seq_printf(s, "  Format: %s\n", source->fmt ? source->fmt : "(null)");
+               seq_printf(s, "  Warnings: %d\n", source->warn_count);
+               
+#if BLOG_TRACK_USAGE
+               seq_printf(s, "  NAPI usage: %d calls, %d bytes\n",
+                         atomic_read(&source->napi_usage),
+                         atomic_read(&source->napi_bytes));
+               seq_printf(s, "  Task usage: %d calls, %d bytes\n",
+                         atomic_read(&source->task_usage),
+                         atomic_read(&source->task_bytes));
+#endif
+               seq_printf(s, "\n");
+       }
+       
+       seq_printf(s, "Total registered sources: %d\n", count);
+       
+       return 0;
+}
+
+static int blog_sources_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, blog_sources_show, inode->i_private);
+}
+
+static const struct file_operations blog_sources_fops = {
+       .open = blog_sources_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+/**
+ * blog_clients_show - Show all registered Ceph clients
+ */
+static int blog_clients_show(struct seq_file *s, void *p)
+{
+       u32 id;
+       int count = 0;
+       const struct ceph_blog_client_info *info;
+       
+       seq_printf(s, "Ceph BLOG Registered Clients\n");
+       seq_printf(s, "=============================\n\n");
+       
+       for (id = 1; id < CEPH_BLOG_MAX_CLIENTS; id++) {
+               info = ceph_blog_get_client_info(id);
+               if (!info || info->global_id == 0)
+                       continue;
+               
+               count++;
+               
+               seq_printf(s, "Client ID %u:\n", id);
+               seq_printf(s, "  FSID: %pU\n", info->fsid);
+               seq_printf(s, "  Global ID: %llu\n", info->global_id);
+               seq_printf(s, "\n");
+       }
+       
+       seq_printf(s, "Total registered clients: %d\n", count);
+       
+       return 0;
+}
+
+static int blog_clients_open(struct inode *inode, struct file *file)
+{
+       return single_open(file, blog_clients_show, inode->i_private);
+}
+
+static const struct file_operations blog_clients_fops = {
+       .open = blog_clients_open,
+       .read = seq_read,
+       .llseek = seq_lseek,
+       .release = single_release,
+};
+
+/**
+ * blog_clear_write - Clear all BLOG entries (write-only)
+ */
+static ssize_t blog_clear_write(struct file *file, const char __user *buf,
+                                size_t count, loff_t *ppos)
+{
+       char cmd[16];
+       
+       if (count >= sizeof(cmd))
+               return -EINVAL;
+       
+       if (copy_from_user(cmd, buf, count))
+               return -EFAULT;
+       
+       cmd[count] = '\0';
+       
+       /* Only accept "clear" command */
+       if (strncmp(cmd, "clear", 5) != 0)
+               return -EINVAL;
+       
+       /* Reset all contexts - this is a simplified version */
+       pr_info("ceph: BLOG entries cleared via debugfs\n");
+       
+       return count;
+}
+
+static const struct file_operations blog_clear_fops = {
+       .write = blog_clear_write,
+};
+
+/**
+ * ceph_blog_debugfs_init - Initialize Ceph BLOG debugfs entries
+ * @parent: Parent debugfs directory (usually ceph root)
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int ceph_blog_debugfs_init(struct dentry *parent)
+{
+       if (!parent)
+               return -EINVAL;
+       
+       /* Create blog subdirectory */
+       ceph_blog_debugfs_dir = debugfs_create_dir("blog", parent);
+       if (!ceph_blog_debugfs_dir)
+               return -ENOMEM;
+       
+       /* Create debugfs entries */
+       debugfs_create_file("entries", 0444, ceph_blog_debugfs_dir, NULL,
+                          &blog_entries_fops);
+       
+       debugfs_create_file("stats", 0444, ceph_blog_debugfs_dir, NULL,
+                          &blog_stats_fops);
+       
+       debugfs_create_file("sources", 0444, ceph_blog_debugfs_dir, NULL,
+                          &blog_sources_fops);
+       
+       debugfs_create_file("clients", 0444, ceph_blog_debugfs_dir, NULL,
+                          &blog_clients_fops);
+       
+       debugfs_create_file("clear", 0200, ceph_blog_debugfs_dir, NULL,
+                          &blog_clear_fops);
+       
+       pr_info("ceph: BLOG debugfs initialized\n");
+       return 0;
+}
+EXPORT_SYMBOL(ceph_blog_debugfs_init);
+
+/**
+ * ceph_blog_debugfs_cleanup - Clean up Ceph BLOG debugfs entries
+ */
+void ceph_blog_debugfs_cleanup(void)
+{
+       debugfs_remove_recursive(ceph_blog_debugfs_dir);
+       ceph_blog_debugfs_dir = NULL;
+       pr_info("ceph: BLOG debugfs cleaned up\n");
+}
+EXPORT_SYMBOL(ceph_blog_debugfs_cleanup);
diff --git a/fs/ceph/blog_integration_example.c b/fs/ceph/blog_integration_example.c
new file mode 100644 (file)
index 0000000..ee54cf2
--- /dev/null
@@ -0,0 +1,155 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Example integration code showing how Ceph uses BLOG
+ * 
+ * This demonstrates the transition from ceph_san to BLOG with preserved
+ * client ID mapping functionality.
+ */
+
+#include <linux/module.h>
+#include <linux/ceph/ceph_debug.h>
+#include <linux/ceph/ceph_blog.h>
+#include <linux/ceph/libceph.h>
+
+/*
+ * Example 1: Simple logging without client context (like dout)
+ * This doesn't store client_id
+ */
+void ceph_example_simple_log(void)
+{
+       int value = 42;
+       const char *status = "active";
+       
+       /* Using BLOG for simple logging */
+       CEPH_BLOG_LOG("Simple log: value=%d status=%s\n", value, status);
+       
+       /* Legacy compatibility - these map to BLOG */
+       CEPH_SAN_LOG("Legacy simple log: value=%d\n", value);
+       
+       /* Traditional dout remains unchanged */
+       dout("Traditional dout: value=%d\n", value);
+}
+
+/*
+ * Example 2: Client-aware logging (like doutc and boutc)
+ * This stores client_id for later deserialization
+ */
+void ceph_example_client_log(struct ceph_client *client)
+{
+       struct ceph_osd_request *req;
+       u64 offset = 0x1000;
+       u64 length = 0x4000;
+       
+       if (!client)
+               return;
+       
+       /* Using BLOG with client context */
+       CEPH_BLOG_LOG_CLIENT(client, "OSD request: offset=%llu length=%llu\n", 
+                            offset, length);
+       
+       /* Legacy compatibility for client logging */
+       CEPH_SAN_LOG_CLIENT(client, "Legacy client log: offset=%llu\n", offset);
+       
+       /* Traditional doutc - shows [fsid global_id] in text logs */
+       doutc(client, "Traditional doutc: processing request\n");
+       
+       /* boutc uses BLOG internally with client context */
+       boutc(client, "Binary log with client: offset=%llu length=%llu\n",
+             offset, length);
+}
+
+/*
+ * Example 3: Demonstrating client ID mapping preservation
+ * 
+ * The client_id mapping is now handled by Ceph, not BLOG.
+ * This preserves the exact functionality of ceph_san_check_client_id.
+ */
+void ceph_example_client_id_mapping(struct ceph_client *client)
+{
+       u32 client_id;
+       const struct ceph_blog_client_info *info;
+       
+       if (!client)
+               return;
+       
+       /* Get or allocate client ID for this Ceph client */
+       client_id = ceph_blog_get_client_id(client);
+       
+       CEPH_BLOG_LOG_CLIENT(client, 
+                            "Client registered with ID %u\n", client_id);
+       
+       /* The mapping is preserved in Ceph's blog_client.c */
+       info = ceph_blog_get_client_info(client_id);
+       if (info) {
+               pr_info("Client %u maps to fsid=%pU global_id=%llu\n",
+                       client_id, info->fsid, info->global_id);
+       }
+}
+
+/*
+ * Example 4: Debugfs integration
+ * 
+ * The debugfs interface uses BLOG's deserialization with Ceph's
+ * client callback to reconstruct the full log entries.
+ */
+void ceph_example_debugfs_usage(void)
+{
+       /*
+        * Debugfs files created by ceph_blog_debugfs_init():
+        * 
+        * /sys/kernel/debug/ceph/blog/entries
+        *   - Shows all BLOG entries with client info deserialized
+        *   - Uses ceph_blog_client_des_callback to format [fsid gid]
+        * 
+        * /sys/kernel/debug/ceph/blog/stats
+        *   - Shows BLOG statistics
+        * 
+        * /sys/kernel/debug/ceph/blog/sources
+        *   - Shows all registered source locations
+        * 
+        * /sys/kernel/debug/ceph/blog/clients
+        *   - Shows all registered Ceph clients with their mappings
+        * 
+        * /sys/kernel/debug/ceph/blog/clear
+        *   - Write-only file to clear all BLOG entries
+        */
+       pr_info("Debugfs available at /sys/kernel/debug/ceph/blog/\n");
+}
+
+/*
+ * Example 5: Module initialization with BLOG
+ */
+static int __init ceph_blog_example_init(void)
+{
+       int ret;
+       
+       /* Initialize Ceph's BLOG integration */
+       ret = ceph_blog_init();
+       if (ret) {
+               pr_err("Failed to initialize Ceph BLOG integration: %d\n", ret);
+               return ret;
+       }
+       
+       pr_info("Ceph BLOG integration example loaded\n");
+       
+       /* Note: In real usage, blog_init() would be called by BLOG module
+        * and ceph_blog_init() would be called by Ceph FS module init
+        */
+       
+       return 0;
+}
+
+static void __exit ceph_blog_example_exit(void)
+{
+       /* Clean up Ceph's BLOG integration */
+       ceph_blog_cleanup();
+       
+       pr_info("Ceph BLOG integration example unloaded\n");
+}
+
+module_init(ceph_blog_example_init);
+module_exit(ceph_blog_example_exit);
+
+MODULE_DESCRIPTION("Ceph BLOG Integration Example");
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ceph Development Team");
index da61987e0447caceedd541a5aca305b8a677b4c2..12c9006f2f0ca5cd20cb5920353a90cdfccd7bb8 100644 (file)
@@ -105,7 +105,7 @@ struct blog_logger {
        unsigned long total_contexts_allocated;
        u64 next_ctx_id;           /* Next context ID to assign */
        spinlock_t ctx_id_lock;    /* Protects context ID counter */
-       struct blog_tls_ctx __percpu *napi_ctxs; /* Per-CPU NAPI contexts */
+       struct blog_tls_ctx * __percpu *napi_ctxs; /* Per-CPU NAPI context pointers */
 };
 
 /* Iterator for log entries in a single pagefrag */
diff --git a/include/linux/blog/blog_module.h b/include/linux/blog/blog_module.h
new file mode 100644 (file)
index 0000000..ac8e8a2
--- /dev/null
@@ -0,0 +1,67 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Binary Logging Infrastructure (BLOG) - Per-Module Support
+ *
+ * This header defines the per-module context support for BLOG.
+ * Each kernel module can have its own isolated logging context.
+ */
+#ifndef _LINUX_BLOG_MODULE_H
+#define _LINUX_BLOG_MODULE_H
+
+#include <linux/blog/blog.h>
+
+/* Per-module context structure */
+struct blog_module_context {
+       char name[32];                      /* Module name */
+       struct blog_logger *logger;         /* Logger instance for this module */
+       void *module_private;               /* Module-specific private data */
+       struct list_head list;              /* List of all module contexts */
+       atomic_t refcount;                  /* Reference count */
+       bool initialized;                   /* Initialization status */
+};
+
+/* Module context management API */
+struct blog_module_context *blog_module_init(const char *module_name);
+void blog_module_cleanup(struct blog_module_context *ctx);
+void blog_module_get(struct blog_module_context *ctx);
+void blog_module_put(struct blog_module_context *ctx);
+
+/* Per-module API functions */
+u32 blog_get_source_id_ctx(struct blog_module_context *ctx, const char *file, 
+                           const char *func, unsigned int line, const char *fmt);
+struct blog_source_info *blog_get_source_info_ctx(struct blog_module_context *ctx, u32 id);
+void* blog_log_ctx(struct blog_module_context *ctx, u32 source_id, u8 client_id, size_t needed_size);
+struct blog_tls_ctx *blog_get_tls_ctx_ctx(struct blog_module_context *ctx);
+struct blog_tls_ctx *blog_get_napi_ctx_ctx(struct blog_module_context *ctx);
+void blog_set_napi_ctx_ctx(struct blog_module_context *ctx, struct blog_tls_ctx *tls_ctx);
+struct blog_tls_ctx *blog_get_ctx_ctx(struct blog_module_context *ctx);
+int blog_log_trim_ctx(struct blog_module_context *ctx, unsigned int n);
+
+/* Helper macros for per-module logging */
+#define __BLOG_LOG_CTX(__ctx, dbg, __client_id, fmt, ...) \
+       do { \
+               static u32 __source_id = 0; \
+               static size_t __size = 0; \
+               void *___buffer = NULL; \
+               if (unlikely(__source_id == 0)) { \
+                       __source_id = blog_get_source_id_ctx(__ctx, kbasename(__FILE__), __func__, __LINE__, fmt); \
+                       __size = blog_cnt(__VA_ARGS__); \
+               } \
+               ___buffer = blog_log_ctx(__ctx, __source_id, __client_id, __size); \
+               if (likely(___buffer) && __size > 0) {  \
+                       void *___tmp = ___buffer; \
+                       size_t actual_size; \
+                       blog_ser(___buffer, ##__VA_ARGS__);\
+                       actual_size = ___buffer - ___tmp; \
+                       blog_log_trim_ctx(__ctx, __size - actual_size); \
+               } \
+       } while (0)
+
+/* Per-module logging macros */
+#define BLOG_LOG_CTX(ctx, fmt, ...) \
+       __BLOG_LOG_CTX(ctx, 0, 0, fmt, ##__VA_ARGS__)
+
+#define BLOG_LOG_CLIENT_CTX(ctx, client_id, fmt, ...) \
+       __BLOG_LOG_CTX(ctx, 0, client_id, fmt, ##__VA_ARGS__)
+
+#endif /* _LINUX_BLOG_MODULE_H */
index f2b0a69f1603aa3cdadb0a889a56f2939fad1d4c..bf136412dbcbf5726a3315831f679a4c38043dc3 100644 (file)
 
 #define char_ptr(str) __suppress_cast_warning(char *, (str))
 
+#ifndef _CEPH_BLOG_SER_HELPERS_DEFINED
+#define _CEPH_BLOG_SER_HELPERS_DEFINED
+
 union null_str_u {
        char str[8];
        unsigned long force_align;
@@ -160,6 +163,8 @@ static inline void* strscpy_n_update(char *dst, const char *src, const char *fil
        return dst + round_up(ret, 4);
 }
 
+#endif /* _CEPH_BLOG_SER_HELPERS_DEFINED */
+
 /* Serialization type macro */
 #define __blog_ser_type(__buffer, __t)                          \
        (__builtin_choose_expr((IS_DYNAMIC_CHAR_PTR((__t)) || IS_STATIC_CHAR_ARRAY((__t))),               \
diff --git a/include/linux/ceph/ceph_blog.h b/include/linux/ceph/ceph_blog.h
new file mode 100644 (file)
index 0000000..dd35a50
--- /dev/null
@@ -0,0 +1,114 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Ceph integration with BLOG (Binary LOGging)
+ *
+ * Provides compatibility layer and Ceph-specific extensions
+ */
+#ifndef CEPH_BLOG_H
+#define CEPH_BLOG_H
+
+#include <linux/blog/blog.h>
+#include <linux/blog/blog_module.h>
+#include <linux/ceph/libceph.h>
+
+/* Client ID mapping structure - preserves ceph_san_client_id fields */
+struct ceph_blog_client_info {
+       char fsid[16];         /* Client FSID */
+       u64 global_id;         /* Client global ID */
+};
+
+/* Constants */
+#define CEPH_BLOG_MAX_CLIENTS 256
+
+/* Ceph's BLOG module context */
+extern struct blog_module_context *ceph_blog_ctx;
+
+/* Ceph BLOG client management functions */
+int ceph_blog_init(void);
+void ceph_blog_cleanup(void);
+u32 ceph_blog_check_client_id(u32 id, const char *fsid, u64 global_id);
+u32 ceph_blog_get_client_id(struct ceph_client *client);
+const struct ceph_blog_client_info *ceph_blog_get_client_info(u32 id);
+int ceph_blog_client_des_callback(char *buf, size_t size, u8 client_id);
+
+/* Compatibility macros for easy migration from ceph_san to BLOG */
+#if defined(CONFIG_BLOG) || defined(CONFIG_BLOG_MODULE)
+
+/* Direct mappings to BLOG functions */
+#define ceph_san_logger_init()          blog_init()
+#define ceph_san_logger_cleanup()       blog_cleanup()
+#define ceph_san_get_source_id          blog_get_source_id
+#define ceph_san_get_source_info        blog_get_source_info
+#define ceph_san_log                    blog_log
+#define ceph_san_get_tls_ctx()          blog_get_tls_ctx()
+#define ceph_san_get_napi_ctx()         blog_get_napi_ctx()
+#define ceph_san_set_napi_ctx(ctx)      blog_set_napi_ctx(ctx)
+#define ceph_san_get_ctx()              blog_get_ctx()
+#define ceph_san_log_trim               blog_log_trim
+
+/* Structure mappings */
+#define ceph_san_logger                 blog_logger
+#define ceph_san_log_entry              blog_log_entry
+#define ceph_san_tls_ctx                blog_tls_ctx
+#define ceph_san_source_info            blog_source_info
+#define ceph_san_log_iter               blog_log_iter
+
+/* 
+ * Ceph-specific logging macros - use Ceph's module context
+ * Note: Only client-aware macros (doutc, boutc) store client_id,
+ * regular macros (dout, bout) do not include client information
+ */
+#define CEPH_BLOG_LOG(fmt, ...) \
+       do { \
+               if (ceph_blog_ctx) \
+                       BLOG_LOG_CTX(ceph_blog_ctx, fmt, ##__VA_ARGS__); \
+       } while (0)
+
+#define CEPH_BLOG_LOG_CLIENT(client, fmt, ...) \
+       do { \
+               if (ceph_blog_ctx) { \
+                       u32 __client_id = ceph_blog_get_client_id(client); \
+                       BLOG_LOG_CLIENT_CTX(ceph_blog_ctx, __client_id, fmt, ##__VA_ARGS__); \
+               } \
+       } while (0)
+
+/* Legacy compatibility - maps old ceph_san macros to BLOG */
+/* Only define if not already defined by ceph_san_logger.h */
+#ifndef CEPH_SAN_LOG
+#define CEPH_SAN_LOG(fmt, ...) \
+       CEPH_BLOG_LOG(fmt, ##__VA_ARGS__)
+#endif
+
+#ifndef CEPH_SAN_LOG_CLIENT
+#define CEPH_SAN_LOG_CLIENT(client, fmt, ...) \
+       CEPH_BLOG_LOG_CLIENT(client, fmt, ##__VA_ARGS__)
+#endif
+
+#else /* !CONFIG_BLOG */
+
+/* Stub macros when BLOG is not enabled */
+#define CEPH_BLOG_LOG(fmt, ...) do {} while (0)
+#define CEPH_BLOG_LOG_CLIENT(client, fmt, ...) do {} while (0)
+#define CEPH_SAN_LOG(fmt, ...) do {} while (0)
+#define CEPH_SAN_LOG_CLIENT(client, fmt, ...) do {} while (0)
+
+/* Stub functions should be static inline, not macros */
+static inline int ceph_blog_init(void) { return 0; }
+static inline void ceph_blog_cleanup(void) { }
+static inline u32 ceph_blog_get_client_id(struct ceph_client *client) { return 0; }
+static inline u32 ceph_blog_check_client_id(u32 id, const char *fsid, u64 global_id) { return 0; }
+static inline const struct ceph_blog_client_info *ceph_blog_get_client_info(u32 id) { return NULL; }
+static inline int ceph_blog_client_des_callback(char *buf, size_t size, u8 client_id) { return 0; }
+
+#endif /* CONFIG_BLOG */
+
+/* Debugfs support */
+#ifdef CONFIG_DEBUG_FS
+int ceph_blog_debugfs_init(struct dentry *parent);
+void ceph_blog_debugfs_cleanup(void);
+#else
+static inline int ceph_blog_debugfs_init(struct dentry *parent) { return 0; }
+static inline void ceph_blog_debugfs_cleanup(void) {}
+#endif
+
+#endif /* CEPH_BLOG_H */
index 365e31b5d2d57d083d05665b3e6d1a1d75e3bbc7..a65e399c4b15b7d259eb0052dc817efc8b005527 100644 (file)
 
 #define char_ptr(str) __suppress_cast_warning(char *, (str))
 
+#ifndef _CEPH_BLOG_SER_HELPERS_DEFINED
+#define _CEPH_BLOG_SER_HELPERS_DEFINED
+
 union null_str_u {
        char str[8];
        unsigned long force_align;
@@ -161,6 +164,8 @@ static inline void* strscpy_n_update(char *dst, const char *src, const char *fil
     return dst + round_up(ret, 4);
 }
 
+#endif /* _CEPH_BLOG_SER_HELPERS_DEFINED */
+
 #define __ceph_san_ser_type(__buffer, __t)                          \
     (__builtin_choose_expr((IS_DYNAMIC_CHAR_PTR((__t)) || IS_STATIC_CHAR_ARRAY((__t))),               \
         /* For static arrays (like __func__), just save pointer */   \
index 7010fb56372b7a22dbc30668255acc59a8b62f78..8c33a2a6e9d5ce770a6a288dcba9ba919e7d5656 100644 (file)
@@ -5,7 +5,7 @@
 
 obj-$(CONFIG_BLOG) += blog.o
 
-blog-y := blog_core.o blog_batch.o blog_pagefrag.o blog_des.o
+blog-y := blog_core.o blog_batch.o blog_pagefrag.o blog_des.o blog_module.o
 
 # Debug support
 # blog-$(CONFIG_BLOG_DEBUG) += blog_debug.o
diff --git a/lib/blog/blog_module.c b/lib/blog/blog_module.c
new file mode 100644 (file)
index 0000000..d5ce2e3
--- /dev/null
@@ -0,0 +1,423 @@
+// SPDX-License-Identifier: GPL-2.0
+/*
+ * Binary Logging Infrastructure (BLOG) - Per-Module Support
+ *
+ * Implements per-module context management for isolated logging.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/list.h>
+#include <linux/blog/blog.h>
+#include <linux/blog/blog_module.h>
+
+/* Global list of all module contexts */
+static LIST_HEAD(blog_module_contexts);
+static DEFINE_SPINLOCK(blog_modules_lock);
+
+/**
+ * blog_module_init - Initialize a per-module BLOG context
+ * @module_name: Name of the module
+ *
+ * Creates an isolated logging context for a specific module.
+ *
+ * Return: Module context on success, NULL on failure
+ */
+struct blog_module_context *blog_module_init(const char *module_name)
+{
+       struct blog_module_context *ctx;
+       struct blog_logger *logger;
+       int i;
+
+       ctx = kzalloc(sizeof(*ctx), GFP_KERNEL);
+       if (!ctx)
+               return NULL;
+
+       logger = kzalloc(sizeof(*logger), GFP_KERNEL);
+       if (!logger) {
+               kfree(ctx);
+               return NULL;
+       }
+
+       /* Initialize module context */
+       strscpy(ctx->name, module_name, sizeof(ctx->name));
+       ctx->logger = logger;
+       atomic_set(&ctx->refcount, 1);
+       ctx->initialized = true;
+       INIT_LIST_HEAD(&ctx->list);
+
+       /* Initialize logger instance */
+       INIT_LIST_HEAD(&logger->contexts);
+       spin_lock_init(&logger->lock);
+       spin_lock_init(&logger->source_lock);
+       spin_lock_init(&logger->ctx_id_lock);
+       atomic_set(&logger->next_source_id, 1);
+       logger->next_ctx_id = 1;
+       logger->total_contexts_allocated = 0;
+
+       /* Initialize batches */
+       blog_batch_init(&logger->alloc_batch);
+       blog_batch_init(&logger->log_batch);
+
+       /* Initialize source map */
+       for (i = 0; i < BLOG_MAX_SOURCE_IDS; i++) {
+               memset(&logger->source_map[i], 0, sizeof(logger->source_map[i]));
+       }
+
+       /* Allocate per-CPU NAPI context pointers */
+       logger->napi_ctxs = alloc_percpu(struct blog_tls_ctx *);
+       if (!logger->napi_ctxs) {
+               kfree(logger);
+               kfree(ctx);
+               return NULL;
+       }
+
+       /* Add to global list */
+       spin_lock(&blog_modules_lock);
+       list_add(&ctx->list, &blog_module_contexts);
+       spin_unlock(&blog_modules_lock);
+
+       pr_info("BLOG: Module context initialized for %s\n", module_name);
+       return ctx;
+}
+EXPORT_SYMBOL(blog_module_init);
+
+/**
+ * blog_module_cleanup - Clean up a module's BLOG context
+ * @ctx: Module context to clean up
+ */
+void blog_module_cleanup(struct blog_module_context *ctx)
+{
+       struct blog_logger *logger;
+       struct blog_tls_ctx *tls_ctx, *tmp;
+
+       if (!ctx || !ctx->initialized)
+               return;
+
+       logger = ctx->logger;
+       if (!logger)
+               return;
+
+       /* Remove from global list */
+       spin_lock(&blog_modules_lock);
+       list_del(&ctx->list);
+       spin_unlock(&blog_modules_lock);
+
+       /* Clean up all TLS contexts */
+       spin_lock(&logger->lock);
+       list_for_each_entry_safe(tls_ctx, tmp, &logger->contexts, list) {
+               list_del(&tls_ctx->list);
+               /* Call release function if set */
+               if (tls_ctx->release)
+                       tls_ctx->release(tls_ctx);
+               kfree(tls_ctx);
+       }
+       spin_unlock(&logger->lock);
+
+       /* Clean up batches */
+       blog_batch_cleanup(&logger->alloc_batch);
+       blog_batch_cleanup(&logger->log_batch);
+
+       /* Free per-CPU NAPI contexts */
+       if (logger->napi_ctxs)
+               free_percpu(logger->napi_ctxs);
+
+       pr_info("BLOG: Module context cleaned up for %s\n", ctx->name);
+
+       kfree(logger);
+       kfree(ctx);
+}
+EXPORT_SYMBOL(blog_module_cleanup);
+
+/**
+ * blog_module_get - Increment module context reference count
+ * @ctx: Module context
+ */
+void blog_module_get(struct blog_module_context *ctx)
+{
+       if (ctx)
+               atomic_inc(&ctx->refcount);
+}
+EXPORT_SYMBOL(blog_module_get);
+
+/**
+ * blog_module_put - Decrement module context reference count
+ * @ctx: Module context
+ */
+void blog_module_put(struct blog_module_context *ctx)
+{
+       if (ctx && atomic_dec_and_test(&ctx->refcount))
+               blog_module_cleanup(ctx);
+}
+EXPORT_SYMBOL(blog_module_put);
+
+/* Per-module API implementations */
+
+/**
+ * blog_get_source_id_ctx - Get or allocate source ID for a module context
+ * @ctx: Module context
+ * @file: Source file name
+ * @func: Function name
+ * @line: Line number
+ * @fmt: Format string
+ *
+ * Return: Source ID
+ */
+u32 blog_get_source_id_ctx(struct blog_module_context *ctx, const char *file,
+                           const char *func, unsigned int line, const char *fmt)
+{
+       struct blog_logger *logger;
+       struct blog_source_info *info;
+       u32 id;
+
+       if (!ctx || !ctx->logger)
+               return 0;
+
+       logger = ctx->logger;
+
+       /* Get next ID */
+       id = atomic_fetch_inc(&logger->next_source_id);
+       if (id >= BLOG_MAX_SOURCE_IDS) {
+               pr_warn("BLOG: Source ID overflow in module %s\n", ctx->name);
+               return 0;
+       }
+
+       /* Fill in source info */
+       spin_lock(&logger->source_lock);
+       info = &logger->source_map[id];
+       info->file = file;
+       info->func = func;
+       info->line = line;
+       info->fmt = fmt;
+       info->warn_count = 0;
+#if BLOG_TRACK_USAGE
+       atomic_set(&info->napi_usage, 0);
+       atomic_set(&info->task_usage, 0);
+       atomic_set(&info->napi_bytes, 0);
+       atomic_set(&info->task_bytes, 0);
+#endif
+       spin_unlock(&logger->source_lock);
+
+       return id;
+}
+EXPORT_SYMBOL(blog_get_source_id_ctx);
+
+/**
+ * blog_get_source_info_ctx - Get source info for an ID in a module context
+ * @ctx: Module context
+ * @id: Source ID
+ *
+ * Return: Source info or NULL
+ */
+struct blog_source_info *blog_get_source_info_ctx(struct blog_module_context *ctx, u32 id)
+{
+       struct blog_logger *logger;
+
+       if (!ctx || !ctx->logger || id >= BLOG_MAX_SOURCE_IDS)
+               return NULL;
+
+       logger = ctx->logger;
+       return &logger->source_map[id];
+}
+EXPORT_SYMBOL(blog_get_source_info_ctx);
+
+/**
+ * blog_get_tls_ctx_ctx - Get or create TLS context for a module
+ * @ctx: Module context
+ *
+ * Return: TLS context or NULL
+ */
+struct blog_tls_ctx *blog_get_tls_ctx_ctx(struct blog_module_context *ctx)
+{
+       struct blog_logger *logger;
+       struct blog_tls_ctx *tls_ctx;
+       struct task_struct *task = current;
+
+       if (!ctx || !ctx->logger)
+               return NULL;
+
+       logger = ctx->logger;
+
+       /* For now, always create a new context per call
+        * TODO: Implement proper per-task TLS storage */
+
+       /* Allocate new TLS context */
+       tls_ctx = kzalloc(sizeof(*tls_ctx), GFP_KERNEL);
+       if (!tls_ctx)
+               return NULL;
+
+       /* Initialize TLS context */
+       INIT_LIST_HEAD(&tls_ctx->list);
+       atomic_set(&tls_ctx->refcount, 1);
+       tls_ctx->task = task;
+       tls_ctx->pid = task->pid;
+       get_task_comm(tls_ctx->comm, task);
+       tls_ctx->base_jiffies = jiffies;
+
+       /* Initialize pagefrag */
+       blog_pagefrag_init(&tls_ctx->pf);
+
+       /* Get unique context ID */
+       spin_lock(&logger->ctx_id_lock);
+       tls_ctx->id = logger->next_ctx_id++;
+       spin_unlock(&logger->ctx_id_lock);
+
+#if BLOG_DEBUG_POISON
+       tls_ctx->debug_poison = BLOG_CTX_POISON;
+#endif
+
+       /* Add to logger's context list */
+       spin_lock(&logger->lock);
+       list_add(&tls_ctx->list, &logger->contexts);
+       logger->total_contexts_allocated++;
+       spin_unlock(&logger->lock);
+
+       /* TODO: Store in task-specific storage */
+
+       return tls_ctx;
+}
+EXPORT_SYMBOL(blog_get_tls_ctx_ctx);
+
+/**
+ * blog_log_ctx - Log a message with module context
+ * @ctx: Module context
+ * @source_id: Source ID
+ * @client_id: Client ID
+ * @needed_size: Size needed for the log entry
+ *
+ * Return: Buffer to write log data to, or NULL on failure
+ */
+void* blog_log_ctx(struct blog_module_context *ctx, u32 source_id, 
+                   u8 client_id, size_t needed_size)
+{
+       struct blog_tls_ctx *tls_ctx;
+       struct blog_log_entry *entry;
+       int alloc;
+       size_t total_size;
+
+       if (!ctx || !ctx->logger)
+               return NULL;
+
+       /* Get TLS context */
+       tls_ctx = blog_get_tls_ctx_ctx(ctx);
+       if (!tls_ctx)
+               return NULL;
+
+       /* Calculate total size needed */
+       total_size = sizeof(*entry) + needed_size;
+       if (total_size > BLOG_LOG_MAX_LEN) {
+               pr_warn_once("BLOG: Log entry too large (%zu > %d) in module %s\n",
+                            total_size, BLOG_LOG_MAX_LEN, ctx->name);
+               return NULL;
+       }
+
+       /* Allocate space from pagefrag */
+       alloc = blog_pagefrag_alloc(&tls_ctx->pf, total_size);
+       if (alloc == -ENOMEM) {
+               pr_debug("blog_log_ctx: allocation failed for module %s\n", ctx->name);
+               blog_pagefrag_reset(&tls_ctx->pf);
+               return NULL;
+       }
+
+       /* Get pointer from allocation */
+       entry = blog_pagefrag_get_ptr(&tls_ctx->pf, alloc);
+       if (!entry) {
+               pr_err("blog_log_ctx: failed to get pointer from pagefrag\n");
+               return NULL;
+       }
+
+       /* Fill in entry header */
+#if BLOG_DEBUG_POISON
+       entry->debug_poison = BLOG_LOG_ENTRY_POISON;
+#endif
+       entry->ts_delta = jiffies - tls_ctx->base_jiffies;
+       entry->source_id = source_id;
+       entry->client_id = client_id;
+       entry->len = needed_size;
+
+       /* Return pointer to buffer area */
+       return entry->buffer;
+}
+EXPORT_SYMBOL(blog_log_ctx);
+
+/**
+ * blog_log_trim_ctx - Trim unused space from last log entry
+ * @ctx: Module context
+ * @n: Number of bytes to trim
+ *
+ * Return: 0 on success, negative on error
+ */
+int blog_log_trim_ctx(struct blog_module_context *ctx, unsigned int n)
+{
+       struct blog_tls_ctx *tls_ctx;
+
+       if (!ctx || !ctx->logger)
+               return -EINVAL;
+
+       tls_ctx = blog_get_tls_ctx_ctx(ctx);
+       if (!tls_ctx)
+               return -EINVAL;
+
+       blog_pagefrag_trim(&tls_ctx->pf, n);
+       return 0;
+}
+EXPORT_SYMBOL(blog_log_trim_ctx);
+
+/**
+ * blog_get_ctx_ctx - Get appropriate context based on execution context
+ * @ctx: Module context
+ *
+ * Return: TLS context or NAPI context depending on execution context
+ */
+struct blog_tls_ctx *blog_get_ctx_ctx(struct blog_module_context *ctx)
+{
+       if (in_serving_softirq())
+               return blog_get_napi_ctx_ctx(ctx);
+       return blog_get_tls_ctx_ctx(ctx);
+}
+EXPORT_SYMBOL(blog_get_ctx_ctx);
+
+/**
+ * blog_get_napi_ctx_ctx - Get NAPI context for current CPU
+ * @ctx: Module context
+ *
+ * Return: NAPI context or NULL
+ */
+struct blog_tls_ctx *blog_get_napi_ctx_ctx(struct blog_module_context *ctx)
+{
+       struct blog_logger *logger;
+       struct blog_tls_ctx **napi_ctx_ptr;
+
+       if (!ctx || !ctx->logger)
+               return NULL;
+
+       logger = ctx->logger;
+       if (!logger->napi_ctxs)
+               return NULL;
+
+       /* Get pointer to the percpu pointer */
+       napi_ctx_ptr = per_cpu_ptr(logger->napi_ctxs, smp_processor_id());
+       return *napi_ctx_ptr;
+}
+EXPORT_SYMBOL(blog_get_napi_ctx_ctx);
+
+/**
+ * blog_set_napi_ctx_ctx - Set NAPI context for current CPU
+ * @ctx: Module context
+ * @tls_ctx: TLS context to set
+ */
+void blog_set_napi_ctx_ctx(struct blog_module_context *ctx, struct blog_tls_ctx *tls_ctx)
+{
+       struct blog_logger *logger;
+       struct blog_tls_ctx **napi_ctx_ptr;
+
+       if (!ctx || !ctx->logger || !ctx->logger->napi_ctxs)
+               return;
+
+       logger = ctx->logger;
+       /* Get pointer to the percpu pointer and set it */
+       napi_ctx_ptr = per_cpu_ptr(logger->napi_ctxs, smp_processor_id());
+       *napi_ctx_ptr = tls_ctx;
+}
+EXPORT_SYMBOL(blog_set_napi_ctx_ctx);