[PATCH v2 03/15] ceph: add BLOG page-fragment allocator
Alex Markuze <[email protected]> Mon, 6 Jul 2026 14:38:31 +0000
| Newsgroups | org.kernel.vger.ceph-devel |
|---|---|
| Message-ID | <[email protected]> |
Add blog_pagefrag.c: lockless page-fragment buffer allocator for per-task BLOG contexts. Signed-off-by: Alex Markuze <[email protected]> --- fs/ceph/blog_pagefrag.c | 95 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 fs/ceph/blog_pagefrag.c diff --git a/fs/ceph/blog_pagefrag.c b/fs/ceph/blog_pagefrag.c new file mode 100644 index 000000000000..bae88ea82aec --- /dev/null +++ b/fs/ceph/blog_pagefrag.c @@ -0,0 +1,95 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Binary Logging Page Fragment Management + */ + +#include <linux/printk.h> +#include <linux/slab.h> +#include <linux/mm.h> +#include <linux/module.h> +#include <linux/ceph/blog_pagefrag.h> + +/** + * blog_pagefrag_reserve - Reserve space in the pagefrag buffer + * @pf: pagefrag allocator + * @n: number of bytes to reserve + * + * Checks if there is sufficient space and returns the current head offset + * WITHOUT advancing the head pointer. This allows the caller to write data + * before making it visible via blog_pagefrag_publish(). + * + * This is lockless - only one writer per pagefrag (per-task context). + * + * Return: offset to reserved memory, or negative error if not enough space + */ +int blog_pagefrag_reserve(struct blog_pagefrag *pf, unsigned int n) +{ + if (n > pf->capacity - pf->head) + return -ENOMEM; /* No space left */ + return pf->head; /* Return offset without advancing */ +} + +/** + * blog_pagefrag_publish - Publish reserved space by advancing head pointer + * @pf: pagefrag allocator + * @publish_head: new head value (offset + bytes_written) + * + * Advances the head pointer to make previously written data visible to + * readers. Uses store-release so that any reader that observes the new + * head value (via load-acquire or under pf->lock) is guaranteed to see + * the preceding entry writes. + * + * Single-writer per pagefrag (per-task context). + */ +void blog_pagefrag_publish(struct blog_pagefrag *pf, u64 publish_head) +{ + pf->alloc_count++; + pf->active_elements++; + /* Release-store pairs with the acquire in readers (pf->lock). */ + smp_store_release(&pf->head, (unsigned int)publish_head); +} + +/** + * blog_pagefrag_get_ptr - Get buffer pointer from pagefrag reserve result + * @pf: pagefrag allocator + * @val: return value from blog_pagefrag_reserve + * + * Return: pointer to reserved buffer region + */ +void *blog_pagefrag_get_ptr(struct blog_pagefrag *pf, u64 val) +{ + char *base = (char *)pf->buffer; + void *rc = base + val; + + if (unlikely(rc < pf->buffer || + rc >= (void *)(base + pf->capacity))) { + pr_err("%s: invalid pointer %llx\n", __func__, + (unsigned long long)(uintptr_t)rc); + WARN_ON_ONCE(1); + return NULL; + } + return rc; +} + +/** + * blog_pagefrag_reset - Reset the pagefrag allocator + * @pf: pagefrag allocator to reset + * + * Resets the head pointer and counters to the beginning of the buffer, + * discarding all stored entries. + * + * Context: Callers must ensure that no writers are active on this + * pagefrag before calling reset. The pagefrag is single-writer and + * writers do not take pf->lock, so resetting while a writer is mid- + * reservation corrupts state. In practice this means the owning task + * must have finished its current blog_log() / blog_log_commit() pair. + */ +void blog_pagefrag_reset(struct blog_pagefrag *pf) +{ + spin_lock(&pf->lock); + pf->head = 0; + pf->active_elements = 0; + pf->alloc_count = 0; + pf->last_entry = NULL; + spin_unlock(&pf->lock); +} -- 2.34.1