[PATCH 07/15] ceph: add Ceph BLOG scaffolding

Alex Markuze <[email protected]>
Newsgroups org.kernel.vger.ceph-devel,org.kernel.vger.linux-kernel
Message-ID <[email protected]>
Wire BLOG into CephFS: ceph_blog.h (journal_info, enter/exit, logging
macros), blog_client.c (per-fsc context lifecycle, client-ID), super.h
(blog_enabled, blog_ctx, debugfs_blog), libceph.h (blog_client_id),
and Makefile (link all BLOG objects into ceph.ko).

Signed-off-by: Alex Markuze <[email protected]>
---
 fs/ceph/Makefile               |   3 +
 fs/ceph/blog_client.c          | 266 ++++++++++++++++++++++++++++++
 fs/ceph/super.h                |   5 +
 include/linux/ceph/ceph_blog.h | 284 +++++++++++++++++++++++++++++++++
 include/linux/ceph/libceph.h   |   2 +
 5 files changed, 560 insertions(+)
 create mode 100644 fs/ceph/blog_client.c
 create mode 100644 include/linux/ceph/ceph_blog.h

diff --git a/fs/ceph/Makefile b/fs/ceph/Makefile
index ebb29d11ac22..d1177badef00 100644
--- a/fs/ceph/Makefile
+++ b/fs/ceph/Makefile
@@ -10,6 +10,9 @@ 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 subvolume_metrics.o
 
+ceph-y += blog_core.o blog_module.o blog_batch.o blog_pagefrag.o blog_des.o
+ceph-y += blog_client.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
index 000000000000..be92b97d2903
--- /dev/null
+++ b/fs/ceph/blog_client.c
@@ -0,0 +1,266 @@
+// 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/ceph/ceph_debug.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/spinlock.h>
+#include <linux/string.h>
+#include <linux/ceph/libceph.h>
+#include <linux/ceph/ceph_blog.h>
+#include <linux/ceph/blog.h>
+
+#include "super.h"
+
+/* Global client mapping state */
+static struct {
+	struct ceph_blog_client_info client_map[CEPH_BLOG_MAX_CLIENTS];
+	u32 next_client_id;
+	spinlock_t lock;  /* protects client_map */
+	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,
+};
+
+static bool ceph_blog_ids_match(const struct ceph_blog_client_info *entry,
+				     const char *fsid, u64 global_id)
+{
+	if (!entry)
+		return false;
+	if (entry->global_id != global_id)
+		return false;
+	return !memcmp(entry->fsid, fsid, sizeof(entry->fsid));
+}
+
+/**
+ * ceph_blog_init - Initialize Ceph BLOG integration
+ *
+ * Initializes the shared client ID mapping state used by per-superblock
+ * BLOG instances.
+ *
+ * Return: 0 on success, negative error code on failure
+ */
+int ceph_blog_init(void)
+{
+	if (!IS_ENABLED(CONFIG_DEBUG_FS))
+		return 0;
+	if (ceph_blog_state.initialized)
+		return 0;
+
+	/* 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_debug("ceph: BLOG client mapping initialized\n");
+	return 0;
+}
+
+/**
+ * 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 (!IS_ENABLED(CONFIG_DEBUG_FS))
+		return;
+	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);
+
+	pr_debug("ceph: BLOG client mapping cleaned up\n");
+}
+
+int ceph_blog_fsc_init(struct ceph_fs_client *fsc)
+{
+	if (!IS_ENABLED(CONFIG_DEBUG_FS))
+		return 0;
+	if (!fsc)
+		return -EINVAL;
+	if (fsc->blog_ctx)
+		return 0;
+
+	fsc->blog_ctx = blog_module_init("ceph");
+	if (!fsc->blog_ctx) {
+		pr_err("ceph: Failed to initialize BLOG context for fs client\n");
+		return -ENOMEM;
+	}
+
+	WRITE_ONCE(fsc->blog_enabled, false);
+	return 0;
+}
+
+void ceph_blog_fsc_cleanup(struct ceph_fs_client *fsc)
+{
+	if (!IS_ENABLED(CONFIG_DEBUG_FS) || !fsc)
+		return;
+
+	WRITE_ONCE(fsc->blog_enabled, false);
+	if (fsc->blog_ctx) {
+		blog_module_cleanup(fsc->blog_ctx);
+		fsc->blog_ctx = NULL;
+	}
+}
+
+bool ceph_blog_is_enabled(struct ceph_fs_client *fsc)
+{
+	return fsc && READ_ONCE(fsc->blog_enabled) &&
+	       fsc->blog_ctx && fsc->blog_ctx->logger;
+}
+
+struct blog_tls_ctx *ceph_blog_acquire_ctx(struct ceph_fs_client *fsc)
+{
+	if (!ceph_blog_is_enabled(fsc))
+		return NULL;
+
+	return blog_get_ctx_ctx(fsc->blog_ctx);
+}
+
+/**
+ * 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
+ *
+ * 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;
+
+	if (unlikely(!ceph_blog_state.initialized)) {
+		WARN_ON_ONCE(1);  /* Should never happen - init_ceph() initializes BLOG */
+		return 0;  /* Drop the log entry */
+	}
+
+	spin_lock(&ceph_blog_state.lock);
+
+	/* Reuse caller-provided hint when it still matches */
+	if (id != 0 && id < CEPH_BLOG_MAX_CLIENTS) {
+		entry = &ceph_blog_state.client_map[id];
+		if (ceph_blog_ids_match(entry, fsid, global_id)) {
+			found_id = id;
+			goto out;
+		}
+	}
+
+	/* Search for an existing entry with matching identity */
+	for (id = 1; id < CEPH_BLOG_MAX_CLIENTS; id++) {
+		entry = &ceph_blog_state.client_map[id];
+		if (ceph_blog_ids_match(entry, fsid, global_id)) {
+			found_id = id;
+			goto out;
+		}
+	}
+
+	/* Assign new identifier; refuse on overflow rather than aliasing */
+	if (ceph_blog_state.next_client_id >= CEPH_BLOG_MAX_CLIENTS - 1) {
+		pr_warn_once("ceph: BLOG client ID space exhausted\n");
+		found_id = 0;
+		goto out;
+	}
+
+	found_id = ceph_blog_state.next_client_id++;
+	entry = &ceph_blog_state.client_map[found_id];
+	memset(entry, 0, sizeof(*entry));
+	memcpy(entry->fsid, fsid, sizeof(entry->fsid));
+	entry->global_id = global_id;
+
+out:
+	spin_unlock(&ceph_blog_state.lock);
+	return found_id;
+}
+
+/**
+ * ceph_blog_get_client_info - Get client info for a given ID
+ * @id: Client ID
+ *
+ * Reads client_map[] without holding ceph_blog_state.lock.
+ * Writers store fields under the lock. Callers accept the benign
+ * race: a torn read may see a partially-updated fsid or global_id,
+ * which only affects the cosmetic prefix in deserialized output.
+ *
+ * Return: Client information for this ID, or NULL if invalid
+ */
+const struct ceph_blog_client_info *ceph_blog_get_client_info(u32 id)
+{
+	if (!READ_ONCE(ceph_blog_state.initialized) ||
+	    id == 0 || id >= CEPH_BLOG_MAX_CLIENTS)
+		return NULL;
+	return &ceph_blog_state.client_map[id];
+}
+
+/**
+ * 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;
+	if (client_id == 0)
+		return 0;
+
+	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 boutc and other Ceph client logging */
+	return snprintf(buf, size, "[%pU %llu] ",
+			info->fsid, info->global_id);
+}
+
+/**
+ * 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)
+{
+	u32 cached;
+	u32 id;
+
+	if (!client)
+		return 0;
+	if (!client->monc.auth)
+		return 0;
+
+	cached = READ_ONCE(client->blog_client_id);
+
+	id = ceph_blog_check_client_id(cached,
+				       client->fsid.fsid,
+				       client->monc.auth->global_id);
+	if (!id)
+		return 0;
+
+	if (READ_ONCE(client->blog_client_id) != id)
+		WRITE_ONCE(client->blog_client_id, id);
+
+	return id;
+}
diff --git a/fs/ceph/super.h b/fs/ceph/super.h
index 1d6aab060780..ecb72c482d0e 100644
--- a/fs/ceph/super.h
+++ b/fs/ceph/super.h
@@ -3,6 +3,7 @@
 #define _FS_CEPH_SUPER_H
 
 #include <linux/ceph/ceph_debug.h>
+#include <linux/ceph/ceph_blog.h>
 #include <linux/ceph/osd_client.h>
 
 #include <linux/unaligned.h>
@@ -154,6 +155,7 @@ struct ceph_fs_client {
 	int mount_state;
 
 	bool blocklisted;
+	bool blog_enabled;
 
 	bool have_copy_from2;
 
@@ -171,6 +173,8 @@ struct ceph_fs_client {
 	DECLARE_HASHTABLE(async_unlink_conflict, CEPH_ASYNC_CREATE_CONFLICT_BITS);
 	spinlock_t async_unlink_conflict_lock;
 
+	struct blog_module_context *blog_ctx;
+
 #ifdef CONFIG_DEBUG_FS
 	struct dentry *debugfs_dentry_lru, *debugfs_caps;
 	struct dentry *debugfs_congestion_kb;
@@ -180,6 +184,7 @@ struct ceph_fs_client {
 	struct dentry *debugfs_mds_sessions;
 	struct dentry *debugfs_metrics_dir;
 	struct dentry *debugfs_reset_dir;
+	struct dentry *debugfs_blog;
 	struct dentry *debugfs_subvolume_metrics;
 #endif
 
diff --git a/include/linux/ceph/ceph_blog.h b/include/linux/ceph/ceph_blog.h
new file mode 100644
index 000000000000..29df4f662a2f
--- /dev/null
+++ b/include/linux/ceph/ceph_blog.h
@@ -0,0 +1,284 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Ceph integration with BLOG (Binary LOGging)
+ *
+ * Provides a shared per-call context (struct ceph_journal_info) cached
+ * in current->journal_info that carries both the BLOG TLS context and
+ * the MDS request pointer used by the xattr / security-label paths.
+ */
+#ifndef CEPH_BLOG_H
+#define CEPH_BLOG_H
+
+#include <linux/ceph/blog.h>
+#include <linux/ceph/blog_module.h>
+#include <linux/ceph/libceph.h>
+
+/* ---------- shared journal_info carrier ---------- */
+
+#define CEPH_JI_MAGIC	0xCE9B7081UL
+
+struct ceph_fs_client;
+
+/**
+ * struct ceph_journal_info - per-call context stashed in journal_info
+ * @magic: CEPH_JI_MAGIC, for safe type-checking when reading journal_info
+ * @saved_ji: previous value of current->journal_info (restored on exit)
+ * @blog_ctx: BLOG TLS context for binary logging, or NULL
+ * @mds_req: MDS request during ceph_fill_trace / readdir_prepopulate,
+ *           or NULL.  Read by xattr.c to avoid deadlocking RPCs and
+ *           to discover which capabilities were already fetched.
+ *
+ * Allocated on the caller's stack at every Ceph VFS entry point.
+ * ceph_blog_enter() installs it in current->journal_info;
+ * ceph_blog_exit() restores the previous value.
+ */
+struct ceph_journal_info {
+	unsigned long		magic;
+	void			*saved_ji;
+	struct ceph_fs_client	*fsc;
+	struct blog_tls_ctx	*blog_ctx;
+	struct ceph_mds_request	*mds_req;
+};
+
+/**
+ * ceph_ji_from_current - safely retrieve ceph_journal_info from journal_info
+ *
+ * Returns the ceph_journal_info pointer if journal_info currently holds
+ * one (magic matches), NULL otherwise.
+ */
+static inline struct ceph_journal_info *ceph_ji_from_current(void)
+{
+	struct ceph_journal_info *ji = current->journal_info;
+
+	if (ji && READ_ONCE(ji->magic) == CEPH_JI_MAGIC)
+		return ji;
+	return NULL;
+}
+
+static inline bool ceph_ji_matches_fsc(const struct ceph_journal_info *ji,
+				       struct ceph_fs_client *fsc)
+{
+	return ji && ji->fsc == fsc;
+}
+
+/**
+ * ceph_current_mds_request - get the in-flight MDS request, if any
+ *
+ * Used by xattr.c to detect whether we are inside ceph_fill_trace()
+ * and to inspect the request's capability mask.  Returns NULL when
+ * called outside that window.
+ */
+static inline struct ceph_mds_request *
+ceph_current_mds_request(struct ceph_fs_client *fsc)
+{
+	struct ceph_journal_info *ji = ceph_ji_from_current();
+
+	return ceph_ji_matches_fsc(ji, fsc) ? ji->mds_req : NULL;
+}
+
+/* ---------- client ID mapping ---------- */
+
+struct ceph_blog_client_info {
+	char fsid[16];
+	u64 global_id;
+};
+
+#define CEPH_BLOG_MAX_CLIENTS 256
+
+struct ceph_client;
+struct ceph_fs_client;
+
+#if IS_ENABLED(CONFIG_CEPH_FS)
+
+int  ceph_blog_init(void);
+void ceph_blog_cleanup(void);
+int  ceph_blog_fsc_init(struct ceph_fs_client *fsc);
+void ceph_blog_fsc_cleanup(struct ceph_fs_client *fsc);
+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);
+bool ceph_blog_is_enabled(struct ceph_fs_client *fsc);
+struct blog_tls_ctx *ceph_blog_acquire_ctx(struct ceph_fs_client *fsc);
+
+/* ---------- entry / exit helpers ---------- */
+
+/**
+ * ceph_blog_enter - call at every Ceph VFS entry point
+ * @fsc: CephFS client that owns this VFS operation
+ * @ji: caller-allocated struct ceph_journal_info (typically on the stack)
+ *
+ * Saves the current journal_info, inherits any active Ceph request
+ * state from the parent frame, and installs @ji as journal_info.
+ * When BLOG tracing is enabled, the outermost Ceph entry acquires and
+ * caches the BLOG context for nested bout()/boutc() callsites.
+ */
+static inline void ceph_blog_enter(struct ceph_fs_client *fsc,
+				   struct ceph_journal_info *ji)
+{
+	struct ceph_journal_info *parent = ceph_ji_from_current();
+
+	ji->magic    = CEPH_JI_MAGIC;
+	ji->saved_ji = current->journal_info;
+	ji->fsc      = fsc;
+	ji->mds_req  = ceph_ji_matches_fsc(parent, fsc) ? parent->mds_req : NULL;
+	ji->blog_ctx = ceph_ji_matches_fsc(parent, fsc) ? parent->blog_ctx : NULL;
+
+	if (!ji->blog_ctx && ceph_blog_is_enabled(fsc))
+		ji->blog_ctx = ceph_blog_acquire_ctx(fsc);
+
+	current->journal_info = ji;
+}
+
+/**
+ * ceph_blog_exit - call at every Ceph VFS exit point
+ * @ji: the same struct passed to ceph_blog_enter()
+ */
+static inline void ceph_blog_exit(struct ceph_journal_info *ji)
+{
+	current->journal_info = ji->saved_ji;
+}
+
+/* ---------- cached-context accessor for boutc hot path ---------- */
+
+static inline struct blog_tls_ctx *ceph_blog_get_cached_ctx(void)
+{
+	struct ceph_journal_info *ji = ceph_ji_from_current();
+
+	if (!ji || !ceph_blog_is_enabled(ji->fsc))
+		return NULL;
+	return ji->blog_ctx;
+}
+
+/* ---------- logging macros ---------- */
+
+#define CEPH_BLOG_LOG(fmt, ...) \
+	do { \
+		static struct blog_source_id_cache __source_cache; \
+		size_t __size = blog_cnt(__VA_ARGS__); \
+		struct blog_tls_ctx *__ctx; \
+		struct blog_logger *__logger; \
+		void *___buffer; \
+		u32 __sid; \
+		__ctx = ceph_blog_get_cached_ctx(); \
+		if (unlikely(!__ctx)) \
+			break; \
+		__logger = __ctx->logger; \
+		if (unlikely(!__logger)) \
+			break; \
+		__sid = blog_get_source_id_cached(__logger, &__source_cache, \
+						  kbasename(__FILE__), __func__, \
+						  __LINE__, fmt); \
+		if (unlikely(__sid == 0)) { \
+			WRITE_ONCE(__source_cache.logger, NULL); \
+			WRITE_ONCE(__source_cache.id, 0); \
+			break; \
+		} \
+		___buffer = blog_log_with_ctx(__logger, __ctx, \
+					      __sid, 0, __size); \
+		if (likely(___buffer)) { \
+			void *___tmp = ___buffer; \
+			if (__size > 0) { \
+				blog_ser(___buffer, ##__VA_ARGS__); \
+			} \
+			blog_log_commit_with_ctx(__logger, __ctx, \
+						 ___buffer - ___tmp); \
+		} \
+	} while (0)
+
+#define CEPH_BLOG_LOG_CLIENT(client, fmt, ...) \
+	do { \
+		static struct blog_source_id_cache __source_cache; \
+		size_t __size = blog_cnt(__VA_ARGS__); \
+		struct blog_tls_ctx *__ctx; \
+		struct blog_logger *__logger; \
+		void *___buffer; \
+		u32 __client_id; \
+		u32 __sid; \
+		__ctx = ceph_blog_get_cached_ctx(); \
+		if (unlikely(!__ctx)) \
+			break; \
+		__logger = __ctx->logger; \
+		if (unlikely(!__logger)) \
+			break; \
+		__sid = blog_get_source_id_cached(__logger, &__source_cache, \
+						  kbasename(__FILE__), __func__, \
+						  __LINE__, fmt); \
+		if (unlikely(__sid == 0)) { \
+			WRITE_ONCE(__source_cache.logger, NULL); \
+			WRITE_ONCE(__source_cache.id, 0); \
+			break; \
+		} \
+		__client_id = ceph_blog_get_client_id(client); \
+		___buffer = blog_log_with_ctx(__logger, __ctx, \
+					      __sid, __client_id, __size); \
+		if (likely(___buffer)) { \
+			void *___tmp = ___buffer; \
+			if (__size > 0) { \
+				blog_ser(___buffer, ##__VA_ARGS__); \
+			} \
+			blog_log_commit_with_ctx(__logger, __ctx, \
+						 ___buffer - ___tmp); \
+		} \
+	} while (0)
+
+#else /* !CONFIG_CEPH_FS */
+
+#define CEPH_BLOG_LOG(fmt, ...)				do {} while (0)
+#define CEPH_BLOG_LOG_CLIENT(client, fmt, ...)		do {} while (0)
+
+static inline int  ceph_blog_init(void) { return 0; }
+static inline void ceph_blog_cleanup(void) { }
+static inline int  ceph_blog_fsc_init(struct ceph_fs_client *fsc) { return 0; }
+static inline void ceph_blog_fsc_cleanup(struct ceph_fs_client *fsc) {}
+static inline u32  ceph_blog_get_client_id(struct ceph_client *c) { 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; }
+static inline bool ceph_blog_is_enabled(struct ceph_fs_client *fsc) { return false; }
+static inline struct blog_tls_ctx *
+ceph_blog_acquire_ctx(struct ceph_fs_client *fsc) { return NULL; }
+
+/*
+ * When BLOG is disabled, ceph_blog_enter/exit still install the
+ * ceph_journal_info so that the mds_req path works unconditionally.
+ */
+static inline void ceph_blog_enter(struct ceph_fs_client *fsc,
+				   struct ceph_journal_info *ji)
+{
+	struct ceph_journal_info *parent = ceph_ji_from_current();
+
+	ji->magic    = CEPH_JI_MAGIC;
+	ji->saved_ji = current->journal_info;
+	ji->fsc      = fsc;
+	ji->mds_req  = ceph_ji_matches_fsc(parent, fsc) ? parent->mds_req : NULL;
+	ji->blog_ctx = NULL;
+	current->journal_info = ji;
+}
+
+static inline void ceph_blog_exit(struct ceph_journal_info *ji)
+{
+	current->journal_info = ji->saved_ji;
+}
+
+static inline struct blog_tls_ctx *ceph_blog_get_cached_ctx(void)
+{
+	return NULL;
+}
+
+#endif /* CONFIG_CEPH_FS */
+
+/* ---------- debugfs ---------- */
+
+#ifdef CONFIG_DEBUG_FS
+int  ceph_blog_debugfs_init(struct ceph_fs_client *fsc);
+void ceph_blog_debugfs_cleanup(struct ceph_fs_client *fsc);
+#else
+static inline int  ceph_blog_debugfs_init(struct ceph_fs_client *fsc) { return 0; }
+static inline void ceph_blog_debugfs_cleanup(struct ceph_fs_client *fsc) {}
+#endif
+
+#endif /* CEPH_BLOG_H */
diff --git a/include/linux/ceph/libceph.h b/include/linux/ceph/libceph.h
index 63e0e2aa1ce9..f853836be298 100644
--- a/include/linux/ceph/libceph.h
+++ b/include/linux/ceph/libceph.h
@@ -133,6 +133,8 @@ struct ceph_client {
 	struct ceph_mon_client monc;
 	struct ceph_osd_client osdc;
 
+	u32 blog_client_id;
+
 #ifdef CONFIG_DEBUG_FS
 	struct dentry *debugfs_dir;
 	struct dentry *debugfs_monmap;
-- 
2.34.1
lmpx.com only provides a reader for public news (NNTP) servers. It is not affiliated with the servers or forums shown here and is not responsible for the content of articles, which is written by their respective authors.