[PATCH v3 03/14] ceph: add BLOG page-fragment allocator
Alex Markuze <[email protected]>
| Newsgroups | org.kernel.vger.ceph-devel |
|---|---|
| Message-ID | <2be13f3f5bdc2e349ff75fcdeeda2f16ef375dde.1787229471.git.amarkuze@redhat.com> |
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 | 84 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 84 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..523cb2d0ba63 --- /dev/null +++ b/fs/ceph/blog_pagefrag.c @@ -0,0 +1,84 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Binary Logging Page Fragment Management + */ + +#include "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, unsigned int publish_head) +{ + /* Release-store pairs with the acquire in readers (pf->lock). */ + smp_store_release(&pf->head, 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))) { + 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; + spin_unlock(&pf->lock); +} -- 2.34.1