From: Emil Tsalapatis <emil@etsalapatis.com>
To: bpf@vger.kernel.org
Cc: ast@kernel.org, andrii@kernel.org, memxor@gmail.com,
daniel@iogearbox.net, eddyz87@gmail.com,
mattbobrowski@google.com, song@kernel.org,
Emil Tsalapatis <emil@etsalapatis.com>
Subject: [PATCH bpf-next 4/5] selftests/bpf: Add arena-based bitmap data structure
Date: Thu, 18 Jun 2026 04:56:25 -0400 [thread overview]
Message-ID: <20260618085626.19633-5-emil@etsalapatis.com> (raw)
In-Reply-To: <20260618085626.19633-1-emil@etsalapatis.com>
Add an arena-based word-aligned bitmap data struture. The
structure is useful as a building block, e.g., sched-ext
uses it to represent cpumask structures.
Signed-off-by: Emil Tsalapatis <emil@etsalapatis.com>
---
.../bpf/libarena/include/libarena/bitmap.h | 31 +++
.../selftests/bpf/libarena/src/bitmap.bpf.c | 191 ++++++++++++++++++
2 files changed, 222 insertions(+)
create mode 100644 tools/testing/selftests/bpf/libarena/include/libarena/bitmap.h
create mode 100644 tools/testing/selftests/bpf/libarena/src/bitmap.bpf.c
diff --git a/tools/testing/selftests/bpf/libarena/include/libarena/bitmap.h b/tools/testing/selftests/bpf/libarena/include/libarena/bitmap.h
new file mode 100644
index 000000000000..11623a82e66a
--- /dev/null
+++ b/tools/testing/selftests/bpf/libarena/include/libarena/bitmap.h
@@ -0,0 +1,31 @@
+#pragma once
+
+#define BITS_PER_BYTE 8
+#define BYTES_TO_BITS(nb) ((nb) * BITS_PER_BYTE)
+
+#define BITS_PER_LONG (sizeof(long) * BITS_PER_BYTE)
+#define BITS_TO_LONGS(nr) (((nr) + BITS_PER_LONG - 1) / BITS_PER_LONG)
+#define BIT_MASK(nr) (1UL << ((nr) % BITS_PER_LONG))
+#define BIT_WORD(nr) ((nr) / BITS_PER_LONG)
+
+struct bitmap {
+ u64 bits[0];
+};
+
+struct bitmap __arena *bmp_alloc(size_t bits);
+void bmp_free(struct bitmap __arena *bmp);
+
+void bmp_set_bit(u32 bit, struct bitmap __arena *bmp);
+void bmp_clear_bit(u32 bit, struct bitmap __arena *bmp);
+bool bmp_test_bit(u32 bit, struct bitmap __arena *bmp);
+bool bmp_test_and_clear_bit(u32 bit, struct bitmap __arena *bmp);
+
+void bmp_clear(size_t bits, struct bitmap __arena *bmp);
+void bmp_and(size_t bits, struct bitmap __arena *dst, struct bitmap __arena *src1, struct bitmap __arena *src2);
+void bmp_or(size_t bits, struct bitmap __arena *dst, struct bitmap __arena *src1, struct bitmap __arena *src2);
+bool bmp_empty(size_t bits, struct bitmap __arena *bmp);
+void bmp_copy(size_t bits, struct bitmap __arena *dst, struct bitmap __arena *src);
+
+bool bmp_intersects(size_t bits, struct bitmap __arena *arg1, struct bitmap __arena *arg2);
+bool bmp_subset(size_t bits, struct bitmap __arena *big, struct bitmap __arena *small);
+void bmp_print(size_t bits, struct bitmap __arena *bmp);
diff --git a/tools/testing/selftests/bpf/libarena/src/bitmap.bpf.c b/tools/testing/selftests/bpf/libarena/src/bitmap.bpf.c
new file mode 100644
index 000000000000..a8ca814aa0c8
--- /dev/null
+++ b/tools/testing/selftests/bpf/libarena/src/bitmap.bpf.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: LGPL-2.1 OR BSD-2-Clause
+/*
+ * Copyright (c) 2025-2026 Meta Platforms, Inc. and affiliates.
+ * Copyright (c) 2025-2026 Emil Tsalapatis <emil@etsalapatis.com>
+ */
+
+#include <libarena/common.h>
+
+#include <libarena/asan.h>
+#include <libarena/bitmap.h>
+
+__weak
+struct bitmap __arena *bmp_alloc(size_t bits)
+{
+ struct bitmap __arena *bmp;
+ size_t size = BITS_TO_LONGS(bits) * sizeof(bmp->bits[0]);
+
+ /* Assume long-aligned masks. */
+ if (bits % BITS_PER_LONG)
+ return NULL;
+
+ bmp = (struct bitmap __arena *)arena_malloc(size);
+ if (!bmp)
+ return NULL;
+
+ bmp_clear(bits, bmp);
+
+ return bmp;
+}
+
+__weak
+void bmp_free(struct bitmap __arena *bmp)
+{
+ arena_free(bmp);
+}
+
+__weak
+void bmp_set_bit(u32 bit, struct bitmap __arena *bmp)
+{
+ bmp->bits[BIT_WORD(bit)] |= BIT_MASK(bit);
+}
+
+__weak
+void bmp_clear_bit(u32 bit, struct bitmap __arena *bmp)
+{
+ bmp->bits[BIT_WORD(bit)] &= ~BIT_MASK(bit);
+}
+
+__weak
+bool bmp_test_bit(u32 bit, struct bitmap __arena *bmp)
+{
+ return bmp->bits[BIT_WORD(bit)] & BIT_MASK(bit);
+}
+
+__weak
+bool bmp_test_and_clear_bit(u32 bit, struct bitmap __arena *bmp)
+{
+ u64 val = BIT_MASK(bit);
+ u32 idx = BIT_WORD(bit);
+ u64 actual;
+
+ do {
+ u64 old = bmp->bits[idx];
+
+ if (!(old & val))
+ return false;
+
+ u64 new = old & ~val;
+ actual = cmpxchg(&bmp->bits[idx], old, new);
+
+ if (actual == old)
+ return true;
+
+ } while (can_loop);
+
+ return false;
+}
+
+__weak
+void bmp_clear(size_t bits, struct bitmap __arena *bmp)
+{
+ size_t nwords = BITS_TO_LONGS(bits);
+ volatile u32 i;
+
+ for (i = zero; i < nwords && can_loop; i++)
+ bmp->bits[i] = 0;
+}
+
+static __always_inline u64 bmp_last_word_mask(size_t bits)
+{
+ u32 rem = bits % BITS_PER_LONG;
+
+ return rem ? (1ULL << rem) - 1 : ~0ULL;
+}
+
+__weak
+void bmp_and(size_t bits, struct bitmap __arena *dst, struct bitmap __arena *src1, struct bitmap __arena *src2)
+{
+ size_t nwords = BITS_TO_LONGS(bits);
+ volatile u32 i;
+
+ for (i = zero; i < nwords && can_loop; i++)
+ dst->bits[i] = src1->bits[i] & src2->bits[i];
+
+ if (nwords && bits % BITS_PER_LONG)
+ dst->bits[nwords - 1] &= bmp_last_word_mask(bits);
+}
+
+__weak
+void bmp_or(size_t bits, struct bitmap __arena *dst, struct bitmap __arena *src1, struct bitmap __arena *src2)
+{
+ size_t nwords = BITS_TO_LONGS(bits);
+ volatile u32 i;
+
+ for (i = zero; i < nwords && can_loop; i++)
+ dst->bits[i] = src1->bits[i] | src2->bits[i];
+
+ if (nwords && bits % BITS_PER_LONG)
+ dst->bits[nwords - 1] &= bmp_last_word_mask(bits);
+}
+
+__weak
+bool bmp_empty(size_t bits, struct bitmap __arena *bmp)
+{
+ size_t nwords = BITS_TO_LONGS(bits);
+ volatile u32 i;
+
+ for (i = zero; i < nwords && can_loop; i++) {
+ u64 mask = (i == nwords - 1) ? bmp_last_word_mask(bits) : ~0ULL;
+
+ if (bmp->bits[i] & mask)
+ return false;
+ }
+
+ return true;
+}
+
+__weak
+void bmp_copy(size_t bits, struct bitmap __arena *dst, struct bitmap __arena *src)
+{
+ size_t nwords = BITS_TO_LONGS(bits);
+ volatile u32 i;
+
+ for (i = zero; i < nwords && can_loop; i++)
+ dst->bits[i] = src->bits[i];
+
+ if (nwords && bits % BITS_PER_LONG)
+ dst->bits[nwords - 1] &= bmp_last_word_mask(bits);
+}
+
+__weak
+bool bmp_subset(size_t bits, struct bitmap __arena *big, struct bitmap __arena *small)
+{
+ size_t nwords = BITS_TO_LONGS(bits);
+ volatile u32 i;
+
+ for (i = zero; i < nwords && can_loop; i++) {
+ u64 mask = (i == nwords - 1) ? bmp_last_word_mask(bits) : ~0ULL;
+
+ if (~big->bits[i] & small->bits[i] & mask)
+ return false;
+ }
+
+ return true;
+}
+
+__weak
+bool bmp_intersects(size_t bits, struct bitmap __arena *arg1, struct bitmap __arena *arg2)
+{
+ size_t nwords = BITS_TO_LONGS(bits);
+ volatile u32 i;
+
+ for (i = zero; i < nwords && can_loop; i++) {
+ u64 mask = (i == nwords - 1) ? bmp_last_word_mask(bits) : ~0ULL;
+
+ if (arg1->bits[i] & arg2->bits[i] & mask)
+ return true;
+ }
+
+ return false;
+}
+
+__weak
+void bmp_print(size_t bits, struct bitmap __arena *bmp)
+{
+ size_t nwords = BITS_TO_LONGS(bits);
+ volatile u32 i;
+
+ for (i = zero; i < nwords && can_loop; i++)
+ arena_stderr("%016llx ", bmp->bits[i]);
+}
--
2.54.0
next prev parent reply other threads:[~2026-06-18 8:56 UTC|newest]
Thread overview: 10+ messages / expand[flat|nested] mbox.gz Atom feed top
2026-06-18 8:56 [PATCH bpf-next 0/5] selftests/bpf: libarena cleanup and bitmap struct Emil Tsalapatis
2026-06-18 8:56 ` [PATCH bpf-next 1/5] libarena/selftests: Replace leftover st_ prefix with test_ Emil Tsalapatis
2026-06-18 8:56 ` [PATCH bpf-next 2/5] selftests/bpf: libarena: Fix can-loop zero variable definition Emil Tsalapatis
2026-06-18 9:09 ` sashiko-bot
2026-06-18 8:56 ` [PATCH bpf-next 3/5] selftests/bpf: libarena: Clean up allocation state before buddy tests Emil Tsalapatis
2026-06-18 9:47 ` bot+bpf-ci
2026-06-18 8:56 ` Emil Tsalapatis [this message]
2026-06-18 9:08 ` [PATCH bpf-next 4/5] selftests/bpf: Add arena-based bitmap data structure sashiko-bot
2026-06-18 9:47 ` bot+bpf-ci
2026-06-18 8:56 ` [PATCH bpf-next 5/5] selftests/bpf: libarena: Add bitmap selftets Emil Tsalapatis
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=20260618085626.19633-5-emil@etsalapatis.com \
--to=emil@etsalapatis.com \
--cc=andrii@kernel.org \
--cc=ast@kernel.org \
--cc=bpf@vger.kernel.org \
--cc=daniel@iogearbox.net \
--cc=eddyz87@gmail.com \
--cc=mattbobrowski@google.com \
--cc=memxor@gmail.com \
--cc=song@kernel.org \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox